Explore workflow diagrams¶
Follow this notebook to load a conditional workflow and compare five diagram types. Start with your first diagram if you are new to the Python API.
To run the notebook, use a Python 3.10+ Jupyter kernel. The installation cell requires package-index access; loading the workflow and rendering images require access to GitHub and the PlantUML server. The documentation site displays saved outputs without executing these requests.
The example uses the conditional-workflows CWL document.
In [1]:
Copied!
%pip install "cwl2puml>=0.48.0" "cwl-loader>=0.27.0" Pillow
%pip install "cwl2puml>=0.48.0" "cwl-loader>=0.27.0" Pillow
Requirement already satisfied: cwl-loader in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (0.25.0) Requirement already satisfied: cwl-upgrader==1.2.15 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from cwl-loader) (1.2.15) Requirement already satisfied: cwl-utils>=0.43 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from cwl-loader) (0.43) Requirement already satisfied: loguru==0.7.3 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from cwl-loader) (0.7.3) Requirement already satisfied: ruamel.yaml<0.20,>=0.16.0 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from cwl-upgrader==1.2.15->cwl-loader) (0.19.1) Requirement already satisfied: schema_salad in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from cwl-upgrader==1.2.15->cwl-loader) (8.10.20260825112551) Requirement already satisfied: packaging in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from cwl-utils>=0.43->cwl-loader) (26.3) Requirement already satisfied: rdflib in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from cwl-utils>=0.43->cwl-loader) (7.6.0) Requirement already satisfied: requests in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from cwl-utils>=0.43->cwl-loader) (2.34.2) Requirement already satisfied: typing-extensions>=4.10.0 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from cwl-utils>=0.43->cwl-loader) (4.16.0) Requirement already satisfied: mistune<3.4,>=3.3.0 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from schema_salad->cwl-upgrader==1.2.15->cwl-loader) (3.3.4) Requirement already satisfied: CacheControl<0.15,>=0.13.1 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from CacheControl[filecache]<0.15,>=0.13.1->schema_salad->cwl-upgrader==1.2.15->cwl-loader) (0.14.4) Requirement already satisfied: mypy_extensions in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from schema_salad->cwl-upgrader==1.2.15->cwl-loader) (1.1.0) Requirement already satisfied: rich-argparse in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from schema_salad->cwl-upgrader==1.2.15->cwl-loader) (1.8.0) Requirement already satisfied: msgpack<2.0.0,>=0.5.2 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from CacheControl<0.15,>=0.13.1->CacheControl[filecache]<0.15,>=0.13.1->schema_salad->cwl-upgrader==1.2.15->cwl-loader) (1.2.2) Requirement already satisfied: filelock>=3.8.0 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from CacheControl[filecache]<0.15,>=0.13.1->schema_salad->cwl-upgrader==1.2.15->cwl-loader) (3.32.6) Requirement already satisfied: pyparsing<4,>=2.1.0 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from rdflib->cwl-utils>=0.43->cwl-loader) (3.3.2) Requirement already satisfied: charset_normalizer<4,>=2 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from requests->cwl-utils>=0.43->cwl-loader) (3.5.1) Requirement already satisfied: idna<4,>=2.5 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from requests->cwl-utils>=0.43->cwl-loader) (3.19) Requirement already satisfied: urllib3<3,>=1.26 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from requests->cwl-utils>=0.43->cwl-loader) (2.7.0) Requirement already satisfied: certifi>=2023.5.7 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from requests->cwl-utils>=0.43->cwl-loader) (2026.7.22) Requirement already satisfied: rich>=11.0.0 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from rich-argparse->schema_salad->cwl-upgrader==1.2.15->cwl-loader) (15.0.0) Requirement already satisfied: markdown-it-py>=2.2.0 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from rich>=11.0.0->rich-argparse->schema_salad->cwl-upgrader==1.2.15->cwl-loader) (4.2.0) Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from rich>=11.0.0->rich-argparse->schema_salad->cwl-upgrader==1.2.15->cwl-loader) (2.21.0) Requirement already satisfied: mdurl~=0.1 in /home/stripodi/.local/share/hatch/env/virtual/cwl2puml/oge6zDjU/cwl2puml/lib/python3.12/site-packages (from markdown-it-py>=2.2.0->rich>=11.0.0->rich-argparse->schema_salad->cwl-upgrader==1.2.15->cwl-loader) (0.1.2) Note: you may need to restart the kernel to use updated packages.
1. Parsing¶
It is needed to read first a CWL document from a remote public URL:
In [2]:
Copied!
from collections.abc import Mapping
from cwl_loader import load_cwl_from_location
from cwl_loader.utils import to_index
from cwl_utils.parser import Process
cwl_document: Process | list[Process] = load_cwl_from_location('https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl')
cwl_index: Mapping[str, Process] = to_index(cwl_document) if isinstance(cwl_document, list) else to_index([cwl_document])
from collections.abc import Mapping
from cwl_loader import load_cwl_from_location
from cwl_loader.utils import to_index
from cwl_utils.parser import Process
cwl_document: Process | list[Process] = load_cwl_from_location('https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl')
cwl_index: Mapping[str, Process] = to_index(cwl_document) if isinstance(cwl_document, list) else to_index([cwl_document])
2026-09-15 19:17:31.145 | DEBUG | cwl_loader:load_cwl_from_location:368 - Loading CWL document from https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl... 2026-09-15 19:17:31.581 | DEBUG | cwl_loader:_load_cwl_from_stream:371 - Reading stream from https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl... 2026-09-15 19:17:31.593 | DEBUG | cwl_loader:load_cwl_from_stream:338 - CWL data of type <class 'ruamel.yaml.comments.CommentedMap'> successfully loaded from stream 2026-09-15 19:17:31.593 | DEBUG | cwl_loader:load_cwl_from_yaml:261 - No needs to update the Raw CWL document since it targets already the v1.2 2026-09-15 19:17:31.594 | DEBUG | cwl_loader:load_cwl_from_yaml:265 - Parsing the raw CWL document to the CWL Utils DOM... 2026-09-15 19:17:34.600 | DEBUG | cwl_loader:load_cwl_from_yaml:274 - Raw CWL document successfully parsed to the CWL Utils DOM! 2026-09-15 19:17:34.601 | DEBUG | cwl_loader:load_cwl_from_yaml:276 - Dereferencing the steps[].run... 2026-09-15 19:17:34.601 | DEBUG | cwl_loader._dereference:_dereference_step:143 - Checking if https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl#stac must be externally imported... 2026-09-15 19:17:34.602 | DEBUG | cwl_loader._dereference:_dereference_step:145 - run_url: https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl - uri: https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl 2026-09-15 19:17:34.602 | DEBUG | cwl_loader._dereference:_dereference_step:143 - Checking if https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl#rio_stack must be externally imported... 2026-09-15 19:17:34.602 | DEBUG | cwl_loader._dereference:_dereference_step:145 - run_url: https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl - uri: https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl 2026-09-15 19:17:34.602 | DEBUG | cwl_loader._dereference:_dereference_step:143 - Checking if https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl#rio_warp_stack must be externally imported... 2026-09-15 19:17:34.602 | DEBUG | cwl_loader._dereference:_dereference_step:145 - run_url: https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl - uri: https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl 2026-09-15 19:17:34.602 | DEBUG | cwl_loader._dereference:_dereference_step:143 - Checking if https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl#rio_color must be externally imported... 2026-09-15 19:17:34.602 | DEBUG | cwl_loader._dereference:_dereference_step:145 - run_url: https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl - uri: https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl 2026-09-15 19:17:34.603 | DEBUG | cwl_loader:load_cwl_from_yaml:285 - steps[].run successfully dereferenced! Dereferencing the FQNs... 2026-09-15 19:17:34.603 | DEBUG | cwl_loader:load_cwl_from_yaml:289 - CWL document successfully dereferenced! Now verifying steps[].run integrity... 2026-09-15 19:17:34.603 | DEBUG | cwl_loader:load_cwl_from_yaml:295 - All steps[].run link are resolvable! 2026-09-15 19:17:34.603 | DEBUG | cwl_loader:load_cwl_from_yaml:298 - Sorting Process instances by dependencies.... 2026-09-15 19:17:34.604 | DEBUG | cwl_loader:load_cwl_from_yaml:300 - Sorting process is over. 2026-09-15 19:17:34.604 | DEBUG | cwl_loader:_load_cwl_from_stream:381 - Stream from https://raw.githubusercontent.com/eoap/how-to/refs/heads/main/cwl-workflows/conditional-workflows.cwl successfully load!
2. PlantUML diagrams¶
The samples below show how to render CWL document to a specific PlantUML diagram:
In [3]:
Copied!
from cwl2puml import (
to_puml,
DiagramType
)
from cwl_utils.parser import InputEnumSchema
from io import (
StringIO,
BytesIO
)
from IPython.display import display_png
from plantuml import deflate_and_encode
from PIL import Image
from urllib.request import urlopen
def render(
diagram_type: DiagramType,
debug: bool = False
):
out = StringIO()
to_puml(
cwl_document=cwl_index,
workflow_id='main', # 'main' by default, but define it for demo purposes
diagram_type=diagram_type,
output_stream=out
)
clear_output = out.getvalue()
if debug:
print(clear_output)
encoded = deflate_and_encode(clear_output)
diagram_url = f"https://uml.planttext.com/plantuml/png/{encoded}"
with urlopen(diagram_url) as url:
img = Image.open(BytesIO(url.read()))
display_png(img)
from cwl2puml import (
to_puml,
DiagramType
)
from cwl_utils.parser import InputEnumSchema
from io import (
StringIO,
BytesIO
)
from IPython.display import display_png
from plantuml import deflate_and_encode
from PIL import Image
from urllib.request import urlopen
def render(
diagram_type: DiagramType,
debug: bool = False
):
out = StringIO()
to_puml(
cwl_document=cwl_index,
workflow_id='main', # 'main' by default, but define it for demo purposes
diagram_type=diagram_type,
output_stream=out
)
clear_output = out.getvalue()
if debug:
print(clear_output)
encoded = deflate_and_encode(clear_output)
diagram_url = f"https://uml.planttext.com/plantuml/png/{encoded}"
with urlopen(diagram_url) as url:
img = Image.open(BytesIO(url.read()))
display_png(img)
In [4]:
Copied!
render(DiagramType.ACTIVITY)
render(DiagramType.ACTIVITY)
In [5]:
Copied!
render(DiagramType.COMPONENT)
render(DiagramType.COMPONENT)
In [6]:
Copied!
render(DiagramType.CLASS)
render(DiagramType.CLASS)
In [7]:
Copied!
render(DiagramType.SEQUENCE)
render(DiagramType.SEQUENCE)
In [8]:
Copied!
render(DiagramType.STATE)
render(DiagramType.STATE)