cwltool
cwltool is the reference implementation for the Common Workflow Language (CWL), enabling users to parse, validate, and execute CWL workflows and command-line tools. It supports various execution environments, including Docker and Singularity. The current version is 3.2.20260411152607, and it maintains a rapid, date-based release cadence, with version numbers reflecting both CWL specification and release date.
Warnings
- breaking Python 3.9 support was dropped in `cwltool` version 3.1.20260108082145. Earlier, Python 3.8 support was removed in 3.1.20241024121129. Users must ensure their environment uses Python 3.10 or newer.
- gotcha Programmatic execution of CWL workflows/tools with `cwltool` can be complex, often requiring the use of `cwltool.main.main` (mimicking CLI calls) or setting up specific `Executor` and `LoadingContext` objects. Directly instantiating `Process` objects (like `CommandLineTool` or `Workflow`) from internal modules is not the recommended or simplest path for execution.
- gotcha The `cwltool` versioning scheme follows a `MAJOR.MINOR.DATE` pattern (e.g., `3.2.20260411152607`). The `MAJOR.MINOR` part typically corresponds to the supported CWL specification version (e.g., `3.2` implies CWL v1.2), while the date reflects the release date. This can sometimes be confusing when tracking API changes independently of CWL specification changes.
- gotcha `cwltool` heavily relies on external container runtimes (like Docker, Singularity/Apptainer, Podman) for executing containerized tools and workflows. Issues with container daemon availability, permissions, or image pulling are common troubleshooting points.
Install
-
pip install cwltool
Imports
- main
from cwltool.main import main
- load_tool
from cwltool.workflow import Workflow # Or from cwltool.process import CommandLineTool
from cwltool.load_tool import load_tool
- LoadingContext
from cwltool.context import LoadingContext
Quickstart
import os
from cwltool.load_tool import load_tool
from cwltool.context import LoadingContext
# 1. Define a simple CWL CommandLineTool
cwl_content = """
cwlVersion: v1.0
class: CommandLineTool
id: my_echo_tool
baseCommand: echo
inputs:
message:
type: string
inputBinding:
position: 1
outputs:
output_message:
type: stdout
stdout: output.txt
"""
# 2. Save it to a temporary file
cwl_file_path = "simple_echo.cwl"
with open(cwl_file_path, "w") as f:
f.write(cwl_content)
try:
# 3. Load and validate the CWL document
# A LoadingContext is often required for proper resolution of includes/imports
# and setting the base URI.
loadingContext = LoadingContext({"$base": os.path.abspath(os.path.dirname(cwl_file_path))})
loadingContext.fileuri = f"file://{os.path.abspath(cwl_file_path)}"
# load_tool returns a Process object (CommandLineTool, Workflow, or ExpressionTool)
tool = load_tool(cwl_file_path, loadingContext)
print(f"Successfully loaded and validated CWL tool: {tool.tool_id}")
print(f"Tool class: {tool.class_}")
print(f"Inputs: {[i.id for i in tool.inputs]}")
print(f"Outputs: {[o.id for o in tool.outputs]}")
# To actually *run* it, you would typically use cwltool.main.main
# or a custom executor with an input object.
# The `load_tool` function is primarily for parsing and validation.
except Exception as e:
print(f"Error loading CWL tool: {e}")
finally:
# Clean up the temporary file
if os.path.exists(cwl_file_path):
os.remove(cwl_file_path)