Skip to content
This repository was archived by the owner on Jun 9, 2020. It is now read-only.

Commit 8d10b9d

Browse files
authored
Merge pull request #7 from bmeg/generate
Working on streamlining process of creating a new workflow
2 parents f609401 + 2219e37 commit 8d10b9d

File tree

6 files changed

+173
-64
lines changed

6 files changed

+173
-64
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ doc/build/
55
build/
66
cwlgen.egg*
77
dist/
8+
*.pyc

cwlgen/__init__.py

Lines changed: 5 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,13 @@
1616
from .version import __version__
1717

1818
from .utils import *
19+
from .elements import Parameter, CWL_VERSIONS
20+
from .workflow import Workflow, File
21+
from .import_cwl import parse_cwl
1922

2023
logging.basicConfig(level=logging.INFO)
2124
LOGGER = logging.getLogger(__name__)
2225

23-
# Constant(s) ------------------------------
24-
25-
CWL_SHEBANG = "#!/usr/bin/env cwl-runner"
26-
CWL_VERSIONS = ['draft-2', 'draft-3.dev1', 'draft-3.dev2', 'draft-3.dev3',
27-
'draft-3.dev4', 'draft-3.dev5', 'draft-3', 'draft-4.dev1',
28-
'draft-4.dev2', 'draft-4.dev3', 'v1.0.dev4', 'v1.0', None]
29-
DEF_VERSION = 'v1.0'
30-
CWL_TYPE = ['null', 'boolean', 'int', 'long', 'float', 'double', 'string', 'File',
31-
'Directory', None]
3226

3327
# Function(s) ------------------------------
3428

@@ -43,7 +37,7 @@ class CommandLineTool(object):
4337
__CLASS__ = 'CommandLineTool'
4438

4539
def __init__(self, tool_id=None, base_command=None, label=None, doc=None,
46-
cwl_version=None, stdin=None, stderr=None, stdout=None):
40+
cwl_version=None, stdin=None, stderr=None, stdout=None, path=None):
4741
'''
4842
:param tool_id: unique identifier for this tool
4943
:type tool_id: STRING
@@ -87,6 +81,7 @@ def __init__(self, tool_id=None, base_command=None, label=None, doc=None,
8781
self.successCodes = []
8882
self.temporaryFailCodes = []
8983
self.permanentFailCodes = []
84+
self._path = path
9085
self.namespaces = Namespaces()
9186

9287
def export(self, outfile=None):
@@ -144,58 +139,6 @@ def export(self, outfile=None):
144139
out_write.close()
145140

146141

147-
class Parameter(object):
148-
'''
149-
A parameter (common field of Input and Output) for a CommandLineTool
150-
'''
151-
152-
def __init__(self, param_id, label=None, secondary_files=None, param_format=None,
153-
streamable=False, doc=None, param_type=None):
154-
'''
155-
:param param_id: unique identifier for this parameter
156-
:type param_id: STRING
157-
:param label: short, human-readable label
158-
:type label: STRING
159-
:param secondary_files: If type is a file, describes files that must be
160-
included alongside the primary file(s)
161-
:type secondary_files: STRING
162-
:param param_format: If type is a file, uri to ontology of the format or exact format
163-
:type param_format: STRING
164-
:param streamable: If type is a file, true indicates that the file is read or written
165-
sequentially without seeking
166-
:type streamable: BOOLEAN
167-
:param doc: documentation
168-
:type doc: STRING
169-
:param param_type: type of data assigned to the parameter
170-
:type param_type: STRING corresponding to CWLType
171-
'''
172-
if param_type not in CWL_TYPE:
173-
LOGGER.warning("The type is incorrect for the parameter.")
174-
param_type = None
175-
self.id = param_id
176-
self.label = label
177-
self.secondaryFiles = secondary_files
178-
self.format = param_format
179-
self.streamable = streamable
180-
self.doc = doc
181-
self.type = param_type
182-
183-
def get_dict(self):
184-
'''
185-
Transform the object to a [DICT] to write CWL.
186-
187-
:return: dictionnary of the object
188-
:rtype: DICT
189-
'''
190-
dict_param = {k: v for k, v in vars(self).items() if v is not None and v is not False}
191-
if dict_param['type'] != 'File':
192-
# Remove what is only for File
193-
for key in ['format', 'secondaryFiles', 'streamable']:
194-
try:
195-
del(dict_param[key])
196-
except KeyError:
197-
pass
198-
return dict_param
199142

200143

201144
class CommandInputParameter(Parameter):

cwlgen/elements.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
2+
3+
# Constant(s) ------------------------------
4+
5+
CWL_SHEBANG = "#!/usr/bin/env cwl-runner"
6+
CWL_VERSIONS = ['draft-2', 'draft-3.dev1', 'draft-3.dev2', 'draft-3.dev3',
7+
'draft-3.dev4', 'draft-3.dev5', 'draft-3', 'draft-4.dev1',
8+
'draft-4.dev2', 'draft-4.dev3', 'v1.0.dev4', 'v1.0', None]
9+
DEF_VERSION = 'v1.0'
10+
CWL_TYPE = ['null', 'boolean', 'int', 'long', 'float', 'double', 'string', 'File',
11+
'Directory', None]
12+
13+
14+
15+
class Parameter(object):
16+
'''
17+
Based class for parameters (common field of Input and Output) for CommandLineTool and Workflow
18+
'''
19+
20+
def __init__(self, param_id, label=None, secondary_files=None, param_format=None,
21+
streamable=False, doc=None, param_type=None):
22+
'''
23+
:param param_id: unique identifier for this parameter
24+
:type param_id: STRING
25+
:param label: short, human-readable label
26+
:type label: STRING
27+
:param secondary_files: If type is a file, describes files that must be
28+
included alongside the primary file(s)
29+
:type secondary_files: STRING
30+
:param param_format: If type is a file, uri to ontology of the format or exact format
31+
:type param_format: STRING
32+
:param streamable: If type is a file, true indicates that the file is read or written
33+
sequentially without seeking
34+
:type streamable: BOOLEAN
35+
:param doc: documentation
36+
:type doc: STRING
37+
:param param_type: type of data assigned to the parameter
38+
:type param_type: STRING corresponding to CWLType
39+
'''
40+
if param_type not in CWL_TYPE:
41+
LOGGER.warning("The type is incorrect for the parameter.")
42+
param_type = None
43+
self.id = param_id
44+
self.label = label
45+
self.secondaryFiles = secondary_files
46+
self.format = param_format
47+
self.streamable = streamable
48+
self.doc = doc
49+
self.type = param_type
50+
51+
def get_dict(self):
52+
'''
53+
Transform the object to a [DICT] to write CWL.
54+
55+
:return: dictionnary of the object
56+
:rtype: DICT
57+
'''
58+
dict_param = {k: v for k, v in vars(self).items() if v is not None and v is not False}
59+
if dict_param['type'] != 'File':
60+
# Remove what is only for File
61+
for key in ['format', 'secondaryFiles', 'streamable']:
62+
try:
63+
del(dict_param[key])
64+
except KeyError:
65+
pass
66+
return dict_param

cwlgen/import_cwl.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ def import_cwl(self, cwl_path):
195195
getattr(self, '_load_{}'.format(key))(tool, element)
196196
except AttributeError:
197197
logger.warning(key + " content is not processed (yet).")
198+
tool._path = cwl_path
198199
return tool
199200

200201
def dict_or_idlist_items(v):
@@ -674,6 +675,7 @@ def import_cwl(self, cwl_path):
674675
getattr(self, '_load_{}'.format(key))(tool, element)
675676
except AttributeError:
676677
logger.warning(key + " workflow content is not processed (yet).")
678+
tool._path = cwl_path
677679
return tool
678680

679681
def _load_id(self, tool, id_el):

cwlgen/workflow.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
import ruamel.yaml
44
import six
55

6-
from . import CWL_SHEBANG, Parameter
6+
from .elements import CWL_SHEBANG, Parameter
77
from .utils import *
88

9-
109
class Workflow(object):
1110
'''
1211
Contain all informations to describe a CWL workflow.
@@ -17,6 +16,7 @@ def __init__(self):
1716
self.steps = []
1817
self.inputs = []
1918
self.outputs = []
19+
self._path = None
2020

2121
def export(self, outfile=None):
2222
"""
@@ -57,6 +57,10 @@ def export(self, outfile=None):
5757
out_write.write(ruamel.yaml.dump(cwl_workflow))
5858
out_write.close()
5959

60+
def add(self, step_id, tool, params):
61+
return StepRun(self, step_id, tool, params)
62+
63+
6064
class InputParameter(Parameter):
6165
def __init__(self, param_id, label=None, secondary_files=None, param_format=None,
6266
streamable=False, doc=None, input_binding=None, default=None, param_type=None):
@@ -138,3 +142,64 @@ def __init__(self, param_id, outputSource, label=None, secondary_files=None, par
138142
secondary_files=secondary_files, param_format=param_format,
139143
streamable=streamable, doc=doc, param_type=param_type)
140144
self.outputSource = outputSource
145+
146+
############################
147+
# Workflow contruction classes
148+
149+
class File:
150+
"""
151+
An abstract file reference used for generating workflows
152+
"""
153+
def __init__(self, path):
154+
self.path = path
155+
156+
class Variable:
157+
"""
158+
An output variable from a workflow step
159+
"""
160+
def __init__(self, workflow, step, name):
161+
self.step = step
162+
self.name = name
163+
self.workflow = workflow
164+
165+
def path(self):
166+
return "%s/%s" % (self.step, self.name)
167+
168+
def store(self):
169+
self.workflow.outputs.append(WorkflowOutputParameter(self.path().replace("/", "_"), outputSource=self.path(), param_type="File"))
170+
return
171+
172+
173+
class StepRun:
174+
"""
175+
Result of adding a step into a workflow
176+
"""
177+
def __init__(self, workflow, id, tool, params):
178+
self.tool = tool
179+
self.workflow = workflow
180+
self.id = id
181+
182+
step = WorkflowStep(id=id, run=tool._path)
183+
workflow.steps.append(step)
184+
185+
for i, j in params.items():
186+
if isinstance(j, six.string_types):
187+
step.inputs.append(WorkflowStepInput(i, default=j))
188+
elif isinstance(j, Variable):
189+
step.inputs.append(WorkflowStepInput(i, src=j.path()))
190+
elif isinstance(j, File):
191+
self.workflow.inputs.append( InputParameter(i, param_type="File") )
192+
step.inputs.append(WorkflowStepInput(i, src=i))
193+
194+
for o in tool.outputs:
195+
step.outputs.append(o.id)
196+
197+
def store_all(self):
198+
for i in self.tool.outputs:
199+
Variable(self.workflow, self.id, i.id).store()
200+
201+
def __getitem__(self, key):
202+
for i in self.tool.outputs:
203+
if i.id == key:
204+
return Variable(self.workflow, self.id, key)
205+
raise KeyError

test/test_unit_workflow.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env python
2+
3+
'''
4+
Unit tests for cwlgen library
5+
'''
6+
7+
# Import ------------------------------
8+
9+
# General libraries
10+
import os
11+
import filecmp
12+
import unittest
13+
14+
# External libraries
15+
import cwlgen
16+
17+
# Class(es) ------------------------------
18+
19+
class TestCommandLineTool(unittest.TestCase):
20+
21+
22+
def test_build(self):
23+
24+
w = cwlgen.Workflow()
25+
tool = cwlgen.parse_cwl("test/import_cwl.cwl")
26+
27+
f = cwlgen.File("input_file")
28+
29+
o1 = w.add('step-a', tool, {"INPUT1" : f})
30+
o2 = w.add('step-b', tool, {"INPUT1" : o1['OUTPUT1']})
31+
o2['OUTPUT1'].store()
32+
w.export("test.cwl")

0 commit comments

Comments
 (0)