Skip to content

Commit 698bd31

Browse files
committed
add figure annotation
1 parent 671b346 commit 698bd31

File tree

5 files changed

+123
-57
lines changed

5 files changed

+123
-57
lines changed

examples/intro.ipynb

Lines changed: 34 additions & 50 deletions
Large diffs are not rendered by default.

examples/new_data.png

-1.36 KB
Loading

ipython2cwl/cwltoolextractor.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from nbformat.notebooknode import NotebookNode # type: ignore
1616

1717
from .iotypes import CWLFilePathInput, CWLBooleanInput, CWLIntInput, CWLStringInput, CWLFilePathOutput, \
18-
CWLDumpableFile, CWLDumpableBinaryFile, CWLDumpable, CWLPNGPlot
18+
CWLDumpableFile, CWLDumpableBinaryFile, CWLDumpable, CWLPNGPlot, CWLPNGFigure
1919
from .requirements_manager import RequirementsManager
2020

2121
with open(os.sep.join([os.path.abspath(os.path.dirname(__file__)), 'templates', 'template.dockerfile'])) as f:
@@ -62,14 +62,19 @@ class AnnotatedVariablesExtractor(ast.NodeTransformer):
6262

6363
dumpable_mapper = {
6464
(CWLDumpableFile.__name__,): (
65-
"with open('{var_name}', 'w') as f:\n\tf.write({var_name})", lambda node: node.target.id
65+
(None, "with open('{var_name}', 'w') as f:\n\tf.write({var_name})",),
66+
lambda node: node.target.id
6667
),
6768
(CWLDumpableBinaryFile.__name__,): (
68-
"with open('{var_name}', 'wb') as f:\n\tf.write({var_name})", lambda node: node.target.id
69+
(None, "with open('{var_name}', 'wb') as f:\n\tf.write({var_name})"),
70+
lambda node: node.target.id
6971
),
7072
(CWLDumpable.__name__, CWLDumpable.dump.__name__): None,
7173
(CWLPNGPlot.__name__,): (
72-
'import matplotlib.pyplot as plt\nplt.savefig("{var_name}.png")',
74+
(None, '{var_name}[-1].figure.savefig("{var_name}.png")'),
75+
lambda node: str(node.target.id) + '.png'),
76+
(CWLPNGFigure.__name__,): (
77+
('import matplotlib.pyplot as plt\nplt.figure()', '{var_name}[-1].figure.savefig("{var_name}.png")'),
7378
lambda node: str(node.target.id) + '.png'),
7479
}
7580

@@ -110,11 +115,18 @@ def _visit_input_ann_assign(self, node, annotation):
110115
return None
111116

112117
def _visit_default_dumper(self, node, dumper):
113-
dump_tree = ast.parse(dumper[0].format(var_name=node.target.id))
118+
if dumper[0][0] is None:
119+
pre_code_body = []
120+
else:
121+
pre_code_body = ast.parse(dumper[0][0].format(var_name=node.target.id)).body
122+
if dumper[0][1] is None:
123+
post_code_body = []
124+
else:
125+
post_code_body = ast.parse(dumper[0][1].format(var_name=node.target.id)).body
114126
self.extracted_variables.append(_VariableNameTypePair(
115127
node.target.id, None, None, None, False, True, dumper[1](node))
116128
)
117-
return [self.conv_AnnAssign_to_Assign(node), *dump_tree.body]
129+
return [*pre_code_body, self.conv_AnnAssign_to_Assign(node), *post_code_body]
118130

119131
def _visit_user_defined_dumper(self, node):
120132
load_ctx = ast.Load()

ipython2cwl/iotypes.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ class CWLDumpableBinaryFile(CWLDumpable):
162162

163163
class CWLPNGPlot(CWLDumpable):
164164
"""Use that annotation to define that after the assigment of that variable the plt.savefig() should
165-
be called
165+
be called.
166166
167167
>>> import matplotlib.pyplot as plt
168168
>>> data = [1,2,3]
@@ -175,5 +175,33 @@ class CWLPNGPlot(CWLDumpable):
175175
>>> new_data: 'CWLPNGPlot' = plt.plot(data)
176176
>>> plt.savefig('new_data.png')
177177
178+
179+
Note that by default if you have multiple plot statements in the same notebook will be written
180+
in the same file. If you want to write them in separates you have to do it in separate figures.
181+
To do that in your notebook you have to create a new figure before the plot command or use the CWLPNGFigure.
182+
183+
>>> import matplotlib.pyplot as plt
184+
>>> data = [1,2,3]
185+
>>> plt.figure()
186+
>>> new_data: 'CWLPNGPlot' = plt.plot(data)
178187
"""
179188
pass
189+
190+
191+
class CWLPNGFigure(CWLDumpable):
192+
"""The same with CWLPNGPlot but creates new figures before plotting. Use that annotation of you don't want
193+
to write multiple graphs in the same image
194+
195+
>>> import matplotlib.pyplot as plt
196+
>>> data = [1,2,3]
197+
>>> new_data: 'CWLPNGPlot' = plt.plot(data)
198+
199+
the converter will tranform these lines to
200+
201+
>>> import matplotlib.pyplot as plt
202+
>>> data = [1,2,3]
203+
>>> plt.figure()
204+
>>> new_data: 'CWLPNGPlot' = plt.plot(data)
205+
>>> plt.savefig('new_data.png')
206+
207+
"""

tests/test_cwltoolextractor.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,3 +516,45 @@ def test_AnnotatedIPython2CWLToolConverter_CWLPNGPlot(self):
516516
},
517517
tool
518518
)
519+
520+
def test_AnnotatedIPython2CWLToolConverter_CWLPNGFigure(self):
521+
code = os.linesep.join([
522+
"import matplotlib.pyplot as plt",
523+
"new_data: 'CWLPNGFigure' = plt.plot([1,2,3,4])",
524+
])
525+
converter = AnnotatedIPython2CWLToolConverter(code)
526+
new_script = converter._wrap_script_to_method(
527+
converter._tree,
528+
converter._variables
529+
)
530+
try:
531+
os.remove('new_data.png')
532+
except FileNotFoundError:
533+
pass
534+
exec(new_script)
535+
locals()['main']()
536+
self.assertTrue(os.path.isfile('new_data.png'))
537+
os.remove('new_data.png')
538+
539+
tool = converter.cwl_command_line_tool()
540+
self.assertDictEqual(
541+
{
542+
'cwlVersion': "v1.1",
543+
'class': 'CommandLineTool',
544+
'baseCommand': 'notebookTool',
545+
'hints': {
546+
'DockerRequirement': {'dockerImageId': 'jn2cwl:latest'}
547+
},
548+
'arguments': ['--'],
549+
'inputs': {},
550+
'outputs': {
551+
'new_data': {
552+
'type': 'File',
553+
'outputBinding': {
554+
'glob': 'new_data.png'
555+
}
556+
}
557+
},
558+
},
559+
tool
560+
)

0 commit comments

Comments
 (0)