Repository: ResearchObject/ro-crate-py
Pull request #150 (2023-05-19 14:26:01)

Remove version from ComputerLanguage
This had several problems:

 * `version` is not a property of [ComputerLanguage](https://schema.org/ComputerLanguage)
 *  We were using defaults, but no info is better than likely wrong info (see also #130)
 * [Workflow RO-Crate language samples](https://about.workflowhub.eu/Workflow-RO-Crate/#supported-workflow-types) also don't have version, though there the CWL identifier is hardcoded to https://w3id.org/cwl/v1.2/.

I left the `lang_version` argument of `add_workflow` untouched, so it can be used to set the version for the CWL identifier.

**UPDATE (2023-05-29):** At the latest meeting, some expressed concerns about removing the ability to set the version entirely, so I changed the PR to only remove the arbitrary defaults instead. Note that the `identifier` for the CWL language is versioned only if a version is provided, as in the original PR.


Patch:
diff --git a/rocrate/model/computerlanguage.py b/rocrate/model/computerlanguage.py
index fdb5872..014bdc0 100644
--- a/rocrate/model/computerlanguage.py
+++ b/rocrate/model/computerlanguage.py
@@ -69,109 +69,116 @@ class ComputerLanguage(ContextEntity):
         self["version"] = version
 
 
-# For workflow ro-crates. Note that
-# https://about.workflowhub.eu/Workflow-RO-Crate/ does not specify versions.
+# See https://w3id.org/workflowhub/workflow-ro-crate/1.0
+# (note that it does not specify "version")
 
-CWL_DEFAULT_VERSION = "1.2"
-# https://github.com/galaxyproject/gxformat2 has some info on gxformat2 versions
-# version can probably be simply ignored for "native" *.ga workflows
-GALAXY_DEFAULT_VERSION = "21.09"
-KNIME_DEFAULT_VERSION = "4.5.0"
-NEXTFLOW_DEFAULT_VERSION = "21.10"
-SNAKEMAKE_DEFAULT_VERSION = "6.13"
-COMPSS_DEFAULT_VERSION = "2.10"
-AUTOSUBMIT_DEFAULT_VERSION = "3.14"
 
-
-def cwl(crate, version=CWL_DEFAULT_VERSION):
+def cwl(crate, version=None):
     id_ = "https://w3id.org/workflowhub/workflow-ro-crate#cwl"
-    return ComputerLanguage(crate, identifier=id_, properties={
+    identifier = "https://w3id.org/cwl/"
+    if version:
+        identifier = f"{identifier}v{version}/"
+    properties = {
         "name": "Common Workflow Language",
         "alternateName": "CWL",
         "identifier": {
-            "@id": f"https://w3id.org/cwl/{version}/"
+            "@id": identifier
         },
         "url": {
             "@id": "https://www.commonwl.org/"
         },
-        "version": version
-    })
+    }
+    if version:
+        properties["version"] = version
+    return ComputerLanguage(crate, identifier=id_, properties=properties)
 
 
-def galaxy(crate, version=GALAXY_DEFAULT_VERSION):
+def galaxy(crate, version=None):
     id_ = "https://w3id.org/workflowhub/workflow-ro-crate#galaxy"
-    return ComputerLanguage(crate, identifier=id_, properties={
+    properties = {
         "name": "Galaxy",
         "identifier": {
             "@id": "https://galaxyproject.org/"
         },
         "url": {
             "@id": "https://galaxyproject.org/"
-        },
-        "version": version
-    })
+        }
+    }
+    if version:
+        properties["version"] = version
+    return ComputerLanguage(crate, identifier=id_, properties=properties)
 
 
-def knime(crate, version=KNIME_DEFAULT_VERSION):
+def knime(crate, version=None):
     id_ = "https://w3id.org/workflowhub/workflow-ro-crate#knime"
-    return ComputerLanguage(crate, identifier=id_, properties={
+    properties = {
         "name": "KNIME",
         "identifier": {
             "@id": "https://www.knime.com/"
         },
         "url": {
             "@id": "https://www.knime.com/"
-        },
-        "version": version
-    })
+        }
+    }
+    if version:
+        properties["version"] = version
+    return ComputerLanguage(crate, identifier=id_, properties=properties)
 
 
-def nextflow(crate, version=NEXTFLOW_DEFAULT_VERSION):
+def nextflow(crate, version=None):
     id_ = "https://w3id.org/workflowhub/workflow-ro-crate#nextflow"
-    return ComputerLanguage(crate, identifier=id_, properties={
+    properties = {
         "name": "Nextflow",
         "identifier": {
             "@id": "https://www.nextflow.io/"
         },
         "url": {
             "@id": "https://www.nextflow.io/"
-        },
-        "version": version
-    })
+        }
+    }
+    if version:
+        properties["version"] = version
+    return ComputerLanguage(crate, identifier=id_, properties=properties)
 
 
-def snakemake(crate, version=SNAKEMAKE_DEFAULT_VERSION):
+def snakemake(crate, version=None):
     id_ = "https://w3id.org/workflowhub/workflow-ro-crate#snakemake"
-    return ComputerLanguage(crate, identifier=id_, properties={
+    properties = {
         "name": "Snakemake",
         "identifier": {
             "@id": "https://doi.org/10.1093/bioinformatics/bts480"
         },
         "url": {
             "@id": "https://snakemake.readthedocs.io"
-        },
-        "version": version
-    })
+        }
+    }
+    if version:
+        properties["version"] = version
+    return ComputerLanguage(crate, identifier=id_, properties=properties)
 
 
-def compss(crate, version=COMPSS_DEFAULT_VERSION):
-    return ComputerLanguage(crate, identifier="#compss", properties={
+def compss(crate, version=None):
+    properties = {
         "name": "COMPSs Programming Model",
         "alternateName": "COMPSs",
         "url": "http://compss.bsc.es/",
-        "citation": "https://doi.org/10.1007/s10723-013-9272-5",
-        "version": version
-    })
+        "citation": "https://doi.org/10.1007/s10723-013-9272-5"
+    }
+    if version:
+        properties["version"] = version
+    return ComputerLanguage(crate, identifier="#compss", properties=properties)
 
 
-def autosubmit(crate, version=AUTOSUBMIT_DEFAULT_VERSION):
-    return ComputerLanguage(crate, identifier="#autosubmit", properties={
+def autosubmit(crate, version=None):
+    properties = {
         "name": "Autosubmit",
         "alternateName": "AS",
         "url": "https://autosubmit.readthedocs.io/",
-        "citation": "https://doi.org/10.1109/HPCSim.2016.7568429",
-        "version": version
-    })
+        "citation": "https://doi.org/10.1109/HPCSim.2016.7568429"
+    }
+    if version:
+        properties["version"] = version
+    return ComputerLanguage(crate, identifier="#autosubmit", properties=properties)
 
 
 LANG_MAP = {
@@ -190,4 +197,4 @@ def get_lang(crate, name, version=None):
         func = LANG_MAP[name.lower()]
     except KeyError:
         raise ValueError(f"Unknown language: {name}")
-    return func(crate, version=version) if version else func(crate)
+    return func(crate, version=version)
diff --git a/rocrate/rocrate.py b/rocrate/rocrate.py
index 79a5955..e209495 100644
--- a/rocrate/rocrate.py
+++ b/rocrate/rocrate.py
@@ -438,8 +438,7 @@ class ROCrate():
         if isinstance(lang, ComputerLanguage):
             assert lang.crate is self
         else:
-            kwargs = {"version": lang_version} if lang_version else {}
-            lang = get_lang(self, lang, **kwargs)
+            lang = get_lang(self, lang, version=lang_version)
             self.add(lang)
         lang_str = lang.id.rsplit("#", 1)[1]
         workflow.lang = lang


Test patch:
diff --git a/test/test_workflow_ro_crate.py b/test/test_workflow_ro_crate.py
index 13d232e..6deed65 100644
--- a/test/test_workflow_ro_crate.py
+++ b/test/test_workflow_ro_crate.py
@@ -16,8 +16,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import pytest
+
 from rocrate.rocrate import ROCrate, make_workflow_rocrate
-from rocrate.model.computerlanguage import CWL_DEFAULT_VERSION, GALAXY_DEFAULT_VERSION
 
 WF_CRATE = "https://w3id.org/workflowhub/workflow-ro-crate"
 
@@ -33,7 +34,7 @@ def test_galaxy_wf_crate(test_data_dir, tmpdir, helpers):
     assert wf_crate.mainEntity is wf
     lang = wf_crate.dereference(f"{WF_CRATE}#galaxy")
     assert hasattr(lang, "name")
-    assert lang.version == GALAXY_DEFAULT_VERSION
+    assert "version" not in lang
     assert wf.get("programmingLanguage") is lang
     assert wf.get("subjectOf") is not None
     assert helpers.WORKFLOW_DESC_TYPES.issubset(wf["subjectOf"].type)
@@ -68,7 +69,7 @@ def test_cwl_wf_crate(test_data_dir, tmpdir, helpers):
     assert wf_crate.mainEntity is wf
     lang = wf_crate.dereference(f"{WF_CRATE}#cwl")
     assert hasattr(lang, "name")
-    assert lang.version == CWL_DEFAULT_VERSION
+    assert "version" not in lang
     assert wf.get("programmingLanguage") is lang
     assert "subjectOf" not in wf
 
@@ -99,7 +100,7 @@ def test_create_wf_include(test_data_dir, tmpdir, helpers):
     assert wf_crate.mainEntity is wf
     lang = wf_crate.dereference(f"{WF_CRATE}#galaxy")
     assert hasattr(lang, "name")
-    assert lang.version == GALAXY_DEFAULT_VERSION
+    assert "version" not in lang
     assert wf.get("programmingLanguage") is lang
     assert wf.get("subjectOf") is not None
     assert helpers.WORKFLOW_DESC_TYPES.issubset(wf["subjectOf"].type)
@@ -124,3 +125,19 @@ def test_create_wf_include(test_data_dir, tmpdir, helpers):
     assert file2.exists()
     with open(extra_file2) as f1, open(file2) as f2:
         assert f1.read() == f2.read()
+
+
+@pytest.mark.parametrize("lang_version", [None, "1.2"])
+def test_cwl_lang_version(test_data_dir, lang_version):
+    wf_id = 'sample_cwl_wf.cwl'
+    wf_path = test_data_dir / wf_id
+    crate = ROCrate()
+    workflow = crate.add_workflow(wf_path, wf_id, lang_version=lang_version)
+    lang = workflow["programmingLanguage"]
+    lang_id = lang["identifier"]
+    if lang_version is None:
+        assert lang_id == "https://w3id.org/cwl/"
+        assert "version" not in lang
+    else:
+        assert lang_id == "https://w3id.org/cwl/v1.2/"
+        assert lang["version"] == "1.2"