Skip to content

Commit a7952c3

Browse files
authored
Merge branch 'master' into feature-js-console-output
2 parents 3223902 + 2f5ddf4 commit a7952c3

File tree

173 files changed

+8291
-1301
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

173 files changed

+8291
-1301
lines changed

README.rst

Lines changed: 67 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,57 @@
11
==================================================================
2-
Common workflow language tool description reference implementation
2+
Common Workflow Language tool description reference implementation
33
==================================================================
44

5-
CWL Conformance test: |Build Status|
6-
7-
Travis: |Unix Build Status|
5+
CWL conformance tests: |Build Status| Travis CI: |Unix Build Status|
86

97
.. |Unix Build Status| image:: https://img.shields.io/travis/common-workflow-language/cwltool/master.svg?label=unix%20build
108
:target: https://travis-ci.org/common-workflow-language/cwltool
119

1210
This is the reference implementation of the Common Workflow Language. It is
13-
intended to be feature complete and provide comprehensive validation of CWL
11+
intended to feature complete and provide comprehensive validation of CWL
1412
files as well as provide other tools related to working with CWL.
1513

16-
This is written and tested for Python 2.7.
14+
This is written and tested for Python ``2.7 and 3.x {x = 3, 4, 5, 6}``
1715

18-
The reference implementation consists of two packages. The "cwltool" package
16+
The reference implementation consists of two packages. The ``cwltool`` package
1917
is the primary Python module containing the reference implementation in the
20-
"cwltool" module and console executable by the same name.
18+
``cwltool`` module and console executable by the same name.
2119

22-
The "cwlref-runner" package is optional and provides an additional entry point
23-
under the alias "cwl-runner", which is the implementation-agnostic name for the
20+
The ``cwlref-runner`` package is optional and provides an additional entry point
21+
under the alias ``cwl-runner``, which is the implementation-agnostic name for the
2422
default CWL interpreter installed on a host.
2523

2624
Install
2725
-------
2826

29-
Installing the official package from PyPi (will install "cwltool" package as
30-
well)::
27+
It is highly recommended to setup virtual environment before installing `cwltool`:
28+
29+
.. code:: bash
30+
31+
virtualenv -p python2 venv # Create a virtual environment, can use `python3` as well
32+
source venv/bin/activate # Activate environment before installing `cwltool`
33+
34+
1. Installing the official package from PyPi (will install "cwltool" package as
35+
well)
36+
37+
.. code:: bash
3138
3239
pip install cwlref-runner
3340
34-
If installing alongside another CWL implementation then::
41+
If installing alongside another CWL implementation then
42+
43+
.. code:: bash
3544
3645
pip install cwltool
3746
38-
To install from source::
47+
2. To install from source
48+
49+
.. code:: bash
3950
40-
git clone https://github.com/common-workflow-language/cwltool.git
41-
cd cwltool && python setup.py install
42-
cd cwlref-runner && python setup.py install # co-installing? skip this
51+
git clone https://github.com/common-workflow-language/cwltool.git # clone cwltool repo
52+
cd cwltool # Switch to source directory
53+
pip install . # Install `cwltool` from source
54+
cwltool --version # Check if the installation works correctly
4355
4456
Remember, if co-installing multiple CWL implementations then you need to
4557
maintain which implementation ``cwl-runner`` points to via a symbolic file
@@ -50,9 +62,15 @@ Running tests locally
5062

5163
- Running basic tests ``(/tests)``:
5264

53-
.. code:: bash
65+
We use `tox <https://github.com/common-workflow-language/cwltool/tree/master/tox.ini>`_
66+
to run various tests in all supported Python environments.
67+
You can run the test suite by simply running the following in the terminal:
68+
``pip install tox; tox``
5469

55-
python setup.py test
70+
List of all environment can be seen using:
71+
``tox --listenvs``
72+
and running a specfic test env using:
73+
``tox -e <env name>``
5674

5775
- Running the entire suite of CWL conformance tests:
5876

@@ -123,13 +141,17 @@ For this example, grab the test.json (and input file) from https://github.com/Ca
123141
Import as a module
124142
------------------
125143

126-
Add::
144+
Add
145+
146+
.. code:: python
127147
128148
import cwltool
129149
130150
to your script.
131151

132-
The easiest way to use cwltool to run a tool or workflow from Python is to use a Factory::
152+
The easiest way to use cwltool to run a tool or workflow from Python is to use a Factory
153+
154+
.. code:: python
133155
134156
import cwltool.factory
135157
fac = cwltool.factory.Factory()
@@ -188,10 +210,11 @@ for the plugin above, this is ``type`` and defines the plugin type. This paramet
188210
is required for all plugins. The available plugins and the parameters
189211
available for each are documented (incompletely) `here
190212
<https://docs.galaxyproject.org/en/latest/admin/dependency_resolvers.html>`__.
191-
Unfortunately, this documentation is in the context of Galaxy tool ``requirement`` s instead of CWL ``SoftwareRequirement`` s, but the concepts map fairly directly.
213+
Unfortunately, this documentation is in the context of Galaxy tool
214+
``requirement`` s instead of CWL ``SoftwareRequirement`` s, but the concepts map fairly directly.
192215

193216
cwltool is distributed with an example of such seqtk tool and sample corresponding
194-
job. It could executed from the cwltool root using a dependency resolvers
217+
job. It could executed from the cwltool root using a dependency resolvers
195218
configuration file such as the above one using the command::
196219

197220
cwltool --beta-dependency-resolvers-configuration /path/to/dependency-resolvers-conf.yml \
@@ -208,8 +231,8 @@ the same concepts - the resolver plugin type ``galaxy_packages`` can be used.
208231
"Galaxy packages" are a lighter weight alternative to Environment Modules that are
209232
really just defined by a way to lay out directories into packages and versions
210233
to find little scripts that are sourced to modify the environment. They have
211-
been used for years in Galaxy community to adapt Galaxy tools to cluster
212-
environments but require neither knowledge of Galaxy nor any special tools to
234+
been used for years in Galaxy community to adapt Galaxy tools to cluster
235+
environments but require neither knowledge of Galaxy nor any special tools to
213236
setup. These should work just fine for CWL tools.
214237

215238
The cwltool source code repository's test directory is setup with a very simple
@@ -238,7 +261,7 @@ Then cwltool will simply find that ``env.sh`` file and source it before executin
238261
the corresponding tool. That ``env.sh`` script is only responsible for modifying
239262
the job's ``PATH`` to add the required binaries.
240263

241-
This is a full example that works since resolving "Galaxy packages" has no
264+
This is a full example that works since resolving "Galaxy packages" has no
242265
external requirements. Try it out by executing the following command from cwltool's
243266
root directory::
244267

@@ -302,7 +325,7 @@ user, install its own Conda environment and manage multiple versions of Conda pa
302325
on both Linux and Mac OS X.
303326

304327
The Conda plugin can be endlessly configured, but a sensible set of defaults
305-
that has proven a powerful stack for dependency management within the Galaxy tool
328+
that has proven a powerful stack for dependency management within the Galaxy tool
306329
development ecosystem can be enabled by simply passing cwltool the
307330
``--beta-conda-dependencies`` flag.
308331

@@ -346,52 +369,52 @@ at the following links:
346369
- `Specifications - Implementation <https://github.com/galaxyproject/galaxy/commit/81d71d2e740ee07754785306e4448f8425f890bc>`__
347370
- `Initial cwltool Integration Pull Request <https://github.com/common-workflow-language/cwltool/pull/214>`__
348371

349-
Cwltool control flow
350-
--------------------
372+
CWL Tool Control Flow
373+
---------------------
351374

352375
Technical outline of how cwltool works internally, for maintainers.
353376

354-
#. Use CWL `load_tool()` to load document.
377+
#. Use CWL ``load_tool()`` to load document.
355378

356379
#. Fetches the document from file or URL
357380
#. Applies preprocessing (syntax/identifier expansion and normalization)
358381
#. Validates the document based on cwlVersion
359382
#. If necessary, updates the document to latest spec
360-
#. Constructs a Process object using `make_tool()` callback. This yields a
383+
#. Constructs a Process object using ``make_tool()``` callback. This yields a
361384
CommandLineTool, Workflow, or ExpressionTool. For workflows, this
362385
recursively constructs each workflow step.
363386
#. To construct custom types for CommandLineTool, Workflow, or
364-
ExpressionTool, provide a custom `make_tool()`
387+
ExpressionTool, provide a custom ``make_tool()``
365388

366-
#. Iterate on the `job()` method of the Process object to get back runnable jobs.
389+
#. Iterate on the ``job()`` method of the Process object to get back runnable jobs.
367390

368-
#. `job()` is a generator method (uses the Python iterator protocol)
369-
#. Each time the `job()` method is invoked in an iteration, it returns one
370-
of: a runnable item (an object with a `run()` method), `None` (indicating
391+
#. ``job()`` is a generator method (uses the Python iterator protocol)
392+
#. Each time the ``job()`` method is invoked in an iteration, it returns one
393+
of: a runnable item (an object with a ``run()`` method), ``None`` (indicating
371394
there is currently no work ready to run) or end of iteration (indicating
372395
the process is complete.)
373-
#. Invoke the runnable item by calling `run()`. This runs the tool and gets output.
396+
#. Invoke the runnable item by calling ``run()``. This runs the tool and gets output.
374397
#. Output of a process is reported by an output callback.
375-
#. `job()` may be iterated over multiple times. It will yield all the work
398+
#. ``job()`` may be iterated over multiple times. It will yield all the work
376399
that is currently ready to run and then yield None.
377400

378-
#. "Workflow" objects create a corresponding "WorkflowJob" and "WorkflowJobStep" objects to hold the workflow state for the duration of the job invocation.
401+
#. ``Workflow`` objects create a corresponding ``WorkflowJob`` and ``WorkflowJobStep`` objects to hold the workflow state for the duration of the job invocation.
379402

380403
#. The WorkflowJob iterates over each WorkflowJobStep and determines if the
381404
inputs the step are ready.
382405
#. When a step is ready, it constructs an input object for that step and
383-
iterates on the `job()` method of the workflow job step.
406+
iterates on the ``job()`` method of the workflow job step.
384407
#. Each runnable item is yielded back up to top level run loop
385408
#. When a step job completes and receives an output callback, the
386409
job outputs are assigned to the output of the workflow step.
387410
#. When all steps are complete, the intermediate files are moved to a final
388411
workflow output, intermediate directories are deleted, and the output
389412
callback for the workflow is called.
390413

391-
#. "CommandLineTool" job() objects yield a single runnable object.
414+
#. ``CommandLineTool`` job() objects yield a single runnable object.
392415

393-
#. The CommandLineTool `job()` method calls `makeJobRunner()` to create a
394-
`CommandLineJob` object
416+
#. The CommandLineTool ``job()`` method calls ``makeJobRunner()`` to create a
417+
``CommandLineJob`` object
395418
#. The job method configures the CommandLineJob object by setting public
396419
attributes
397420
#. The job method iterates over file and directories inputs to the
@@ -402,7 +425,7 @@ Technical outline of how cwltool works internally, for maintainers.
402425
#. Files are staged to targets paths using either Docker volume binds (when
403426
using containers) or symlinks (if not). This staging step enables files
404427
to be logically rearranged or renamed independent of their source layout.
405-
#. The run() method of CommandLineJob executes the command line tool or
428+
#. The ``run()`` method of CommandLineJob executes the command line tool or
406429
Docker container, waits for it to complete, collects output, and makes
407430
the output callback.
408431

appveyor.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
version: .{build}-{branch}
2+
3+
environment:
4+
SYSTEMROOT: "C:\\WINDOWS"
5+
6+
matrix:
7+
- PYTHON: "C:\\Python27"
8+
PYTHON_VERSION: "2.7.x"
9+
PYTHON_ARCH: "32"
10+
11+
- PYTHON: "C:\\Python27-x64"
12+
PYTHON_VERSION: "2.7.x"
13+
PYTHON_ARCH: "64"
14+
15+
- PYTHON: "C:\\Python33-x64"
16+
PYTHON_VERSION: "3.3.x"
17+
PYTHON_ARCH: "64"
18+
19+
- PYTHON: "C:\\Python34-x64"
20+
PYTHON_VERSION: "3.4.x"
21+
PYTHON_ARCH: "64"
22+
23+
- PYTHON: "C:\\Python35-x64"
24+
PYTHON_VERSION: "3.5.x"
25+
PYTHON_ARCH: "64"
26+
27+
28+
install:
29+
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
30+
- "python --version"
31+
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
32+
33+
build_script:
34+
- "%CMD_IN_ENV% pip install ."
35+
36+
37+
test_script:
38+
39+
- "%CMD_IN_ENV% python setup.py test"
40+
41+
branches:
42+
only:
43+
- master

cwltool/builder.py

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import absolute_import
22
import copy
3+
import os
4+
import logging
35
from typing import Any, Callable, Dict, List, Text, Type, Union
46

57
import six
@@ -15,11 +17,10 @@
1517
from .pathmapper import (PathMapper, get_listing, normalizeFilesDirs,
1618
visit_class)
1719
from .stdfsaccess import StdFsAccess
18-
from .utils import aslist
20+
from .utils import aslist, get_feature, docker_windows_path_adjust, onWindows
21+
22+
_logger = logging.getLogger("cwltool")
1923

20-
# if six.PY3:
21-
# AvroSchemaFromJSONData = avro.schema.SchemaFromJSONData
22-
# else:
2324
AvroSchemaFromJSONData = avro.schema.make_avsc_object
2425

2526
CONTENT_LIMIT = 64 * 1024
@@ -52,6 +53,7 @@ def __init__(self): # type: () -> None
5253
self.debug = False # type: bool
5354
self.js_console = False # type: bool
5455
self.mutation_manager = None # type: MutationManager
56+
self.force_docker_pull = False # type: bool
5557

5658
# One of "no_listing", "shallow_listing", "deep_listing"
5759
# Will be default "no_listing" for CWL v1.1
@@ -61,8 +63,8 @@ def __init__(self): # type: () -> None
6163
self.job_script_provider = None # type: Any
6264

6365
def build_job_script(self, commands):
64-
# type: (List[bytes]) -> Text
65-
build_job_script_method = getattr(self.job_script_provider, "build_job_script", None) # type: Callable[[Builder, List[bytes]], Text]
66+
# type: (List[Text]) -> Text
67+
build_job_script_method = getattr(self.job_script_provider, "build_job_script", None) # type: Callable[[Builder, Union[List[str],List[Text]]], Text]
6668
if build_job_script_method:
6769
return build_job_script_method(self, commands)
6870
else:
@@ -148,18 +150,25 @@ def bind_input(self, schema, datum, lead_pos=None, tail_pos=None):
148150
datum["secondaryFiles"] = []
149151
for sf in aslist(schema["secondaryFiles"]):
150152
if isinstance(sf, dict) or "$(" in sf or "${" in sf:
151-
secondary_eval = self.do_eval(sf, context=datum)
152-
if isinstance(secondary_eval, string_types):
153-
sfpath = {"location": secondary_eval,
154-
"class": "File"}
155-
else:
156-
sfpath = secondary_eval
157-
else:
158-
sfpath = {"location": substitute(datum["location"], sf), "class": "File"}
159-
if isinstance(sfpath, list):
160-
datum["secondaryFiles"].extend(sfpath)
153+
sfpath = self.do_eval(sf, context=datum)
161154
else:
162-
datum["secondaryFiles"].append(sfpath)
155+
sfpath = substitute(datum["basename"], sf)
156+
for sfname in aslist(sfpath):
157+
found = False
158+
for d in datum["secondaryFiles"]:
159+
if not d.get("basename"):
160+
d["basename"] = d["location"][d["location"].rindex("/")+1:]
161+
if d["basename"] == sfname:
162+
found = True
163+
if not found:
164+
if isinstance(sfname, dict):
165+
datum["secondaryFiles"].append(sfname)
166+
else:
167+
datum["secondaryFiles"].append({
168+
"location": datum["location"][0:datum["location"].rindex("/")+1]+sfname,
169+
"basename": sfname,
170+
"class": "File"})
171+
163172
normalizeFilesDirs(datum["secondaryFiles"])
164173

165174
def _capture_files(f):
@@ -186,6 +195,11 @@ def tostr(self, value): # type: (Any) -> Text
186195
if isinstance(value, dict) and value.get("class") in ("File", "Directory"):
187196
if "path" not in value:
188197
raise WorkflowException(u"%s object missing \"path\": %s" % (value["class"], value))
198+
199+
# Path adjust for windows file path when passing to docker, docker accepts unix like path only
200+
(docker_req, docker_is_req) = get_feature(self, "DockerRequirement")
201+
if onWindows() and docker_req is not None: # docker_req is none only when there is no dockerRequirement mentioned in hints and Requirement
202+
return docker_windows_path_adjust(value["path"])
189203
return value["path"]
190204
else:
191205
return Text(value)
@@ -244,4 +258,5 @@ def do_eval(self, ex, context=None, pull_image=True, recursive=False):
244258
context=context, pull_image=pull_image,
245259
timeout=self.timeout,
246260
debug=self.debug,
247-
js_console=self.js_console)
261+
js_console=self.js_console,
262+
force_docker_pull=self.force_docker_pull)

cwltool/cwlrdf.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ def visitor(t):
2020
return g
2121

2222

23-
def printrdf(wf, ctx, sr, stdout):
24-
# type: (Process, ContextType, Text, IO[Any]) -> None
25-
stdout.write(gather(wf, ctx).serialize(format=sr))
23+
def printrdf(wf, ctx, sr):
24+
# type: (Process, ContextType, Text) -> Text
25+
return gather(wf, ctx).serialize(format=sr).decode('utf-8')
2626

2727

2828
def lastpart(uri): # type: (Any) -> Text

0 commit comments

Comments
 (0)