{"id":32139028,"url":"https://github.com/researchobject/ro-crate-py","last_synced_at":"2026-04-13T11:01:23.026Z","repository":{"id":39626974,"uuid":"216605684","full_name":"ResearchObject/ro-crate-py","owner":"ResearchObject","description":"Python library for RO-Crate","archived":false,"fork":false,"pushed_at":"2026-02-03T10:15:27.000Z","size":1461,"stargazers_count":80,"open_issues_count":21,"forks_count":33,"subscribers_count":36,"default_branch":"master","last_synced_at":"2026-03-09T06:52:01.251Z","etag":null,"topics":["biohackcovid20","biohackeu20","cwl","galaxy","metadata","researchobject","ro-crate"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/rocrate/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ResearchObject.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2019-10-21T15:44:52.000Z","updated_at":"2026-02-10T23:47:24.000Z","dependencies_parsed_at":"2023-12-21T12:18:48.140Z","dependency_job_id":"8be6dde1-bddc-4bf7-b793-52668943e5d7","html_url":"https://github.com/ResearchObject/ro-crate-py","commit_stats":{"total_commits":449,"total_committers":13,"mean_commits":34.53846153846154,"dds":"0.45657015590200445","last_synced_commit":"1e4687a0fb07a56e426a0cb876300de38e54e09c"},"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"purl":"pkg:github/ResearchObject/ro-crate-py","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ResearchObject%2Fro-crate-py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ResearchObject%2Fro-crate-py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ResearchObject%2Fro-crate-py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ResearchObject%2Fro-crate-py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ResearchObject","download_url":"https://codeload.github.com/ResearchObject/ro-crate-py/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ResearchObject%2Fro-crate-py/sbom","scorecard":{"id":119893,"data":{"date":"2025-08-11","repo":{"name":"github.com/ResearchObject/ro-crate-py","commit":"5bea2eec05ef9986296f0204ced47f9a68f406f3"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.6,"checks":[{"name":"Maintained","score":10,"reason":"13 commit(s) and 3 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/python-package.yml:1","Warn: no topLevel permission defined: .github/workflows/python-publish.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":2,"reason":"Found 2/7 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-package.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/ResearchObject/ro-crate-py/python-package.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-package.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/ResearchObject/ro-crate-py/python-package.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-package.yml:46: update your workflow using https://app.stepsecurity.io/secureworkflow/ResearchObject/ro-crate-py/python-package.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-package.yml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/ResearchObject/ro-crate-py/python-package.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-publish.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/ResearchObject/ro-crate-py/python-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-publish.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/ResearchObject/ro-crate-py/python-publish.yml/master?enable=pin","Warn: containerImage not pinned by hash: Dockerfile:1: pin your Docker image by updating python:3.12 to python:3.12@sha256:645df645815f1403566b103b2a2bb07f6a01516bbb15078ed004e41d198ba194","Warn: pipCommand not pinned by hash: Dockerfile:6-7","Warn: pipCommand not pinned by hash: .github/workflows/python-package.yml:34","Warn: pipCommand not pinned by hash: .github/workflows/python-package.yml:35","Warn: pipCommand not pinned by hash: .github/workflows/python-package.yml:36","Warn: pipCommand not pinned by hash: .github/workflows/python-package.yml:53","Warn: pipCommand not pinned by hash: .github/workflows/python-package.yml:54","Warn: pipCommand not pinned by hash: .github/workflows/python-package.yml:55","Warn: pipCommand not pinned by hash: .github/workflows/python-publish.yml:23","Warn: pipCommand not pinned by hash: .github/workflows/python-publish.yml:24","Warn: pipCommand not pinned by hash: .github/workflows/python-publish.yml:25","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 containerImage dependencies pinned","Info:   0 out of  10 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"16 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2021-100 / GHSA-8h2j-cgx8-6xv7","Warn: Project is vulnerable to: PYSEC-2024-38","Warn: Project is vulnerable to: PYSEC-2014-14 / GHSA-652x-xj99-gmcc","Warn: Project is vulnerable to: GHSA-9hjg-9r4m-mvj7","Warn: Project is vulnerable to: GHSA-9wx4-h78v-vm56","Warn: Project is vulnerable to: PYSEC-2014-13 / GHSA-cfj3-7x9c-4p3h","Warn: Project is vulnerable to: PYSEC-2018-28 / GHSA-x84v-xcm2-53pg","Warn: Project is vulnerable to: PYSEC-2019-217 / GHSA-462w-v97r-4m45","Warn: Project is vulnerable to: PYSEC-2014-8 / GHSA-8r7q-cvjq-x353","Warn: Project is vulnerable to: GHSA-cpwx-vrp4-4pq7","Warn: Project is vulnerable to: PYSEC-2014-82 / GHSA-fqh9-2qgg-h84h","Warn: Project is vulnerable to: PYSEC-2021-66 / GHSA-g3rq-g295-4j3m","Warn: Project is vulnerable to: GHSA-h5c8-rqwp-cp95","Warn: Project is vulnerable to: GHSA-h75v-3vvj-5mfj","Warn: Project is vulnerable to: PYSEC-2019-220 / GHSA-hj2j-77xm-mc5v","Warn: Project is vulnerable to: GHSA-q2x7-8rv6-6q7h"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 29 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-16T02:14:55.192Z","repository_id":39626974,"created_at":"2025-08-16T02:14:55.192Z","updated_at":"2025-08-16T02:14:55.192Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31749763,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-13T09:16:15.125Z","status":"ssl_error","status_checked_at":"2026-04-13T09:16:05.023Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["biohackcovid20","biohackeu20","cwl","galaxy","metadata","researchobject","ro-crate"],"created_at":"2025-10-21T05:13:42.946Z","updated_at":"2026-04-13T11:01:23.011Z","avatar_url":"https://github.com/ResearchObject.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Python package](https://github.com/ResearchObject/ro-crate-py/workflows/Python%20package/badge.svg)](https://github.com/ResearchObject/ro-crate-py/actions?query=workflow%3A%22Python+package%22) [![Upload Python Package](https://github.com/ResearchObject/ro-crate-py/workflows/Upload%20Python%20Package/badge.svg)](https://github.com/ResearchObject/ro-crate-py/actions?query=workflow%3A%22Upload+Python+Package%22) [![PyPI version](https://badge.fury.io/py/rocrate.svg)](https://pypi.org/project/rocrate/) [![DOI](https://zenodo.org/badge/216605684.svg)](https://zenodo.org/badge/latestdoi/216605684)\n\nro-crate-py is a Python library to create and consume [Research Object Crates](https://w3id.org/ro/crate). It supports the current [RO-Crate 1.2](https://w3id.org/ro/crate/1.2) specification as well as the older [RO-Crate 1.1](https://w3id.org/ro/crate/1.1) and [RO-Crate 1.0](https://w3id.org/ro/crate/1.0).\n\n## Installation\n\nro-crate-py requires Python 3.9 or later. The easiest way to install is via [pip](https://docs.python.org/3/installing/):\n\n```\npip install rocrate\n```\n\nTo install the package with support for converting Galaxy workflows to CWL:\n\n```\npip install rocrate[ga2cwl]\n```\n\nTo install manually from this code base (e.g., to try the latest development revision):\n\n```\ngit clone https://github.com/ResearchObject/ro-crate-py\ncd ro-crate-py\npip install .\n```\n\n## Usage\n\n### Creating an RO-Crate\n\nIn its simplest form, an RO-Crate is a directory tree with an `ro-crate-metadata.json` file at the top level. This file contains metadata about the other files and directories, represented by [data entities](https://www.researchobject.org/ro-crate/1.1/data-entities.html). These metadata consist both of properties of the data entities themselves and of other, non-digital entities called [contextual entities](https://www.researchobject.org/ro-crate/1.1/contextual-entities.html). A contextual entity can represent, for instance, a person, an organization or an event.\n\nSuppose Alice and Bob worked on a research task together, which resulted in a manuscript written by both; additionally, Alice prepared a spreadsheet containing the experimental data, which Bob used to generate a diagram. We will create placeholder files for these documents:\n\n```bash\nmkdir exp\ntouch exp/paper.pdf\ntouch exp/results.csv\ntouch exp/diagram.svg\n```\n\nLet's make an RO-Crate to package all this:\n\n```python\nfrom rocrate.rocrate import ROCrate\n\ncrate = ROCrate()\npaper = crate.add_file(\"exp/paper.pdf\", properties={\n    \"name\": \"manuscript\",\n    \"encodingFormat\": \"application/pdf\"\n})\ntable = crate.add_file(\"exp/results.csv\", properties={\n    \"name\": \"experimental data\",\n    \"encodingFormat\": \"text/csv\"\n})\ndiagram = crate.add_file(\"exp/diagram.svg\", dest_path=\"images/figure.svg\", properties={\n    \"name\": \"bar chart\",\n    \"encodingFormat\": \"image/svg+xml\"\n})\n```\n\nThe `dest_path` argument is used to specify the relative path of the file with respect to the crate's directory (which will be determined when the crate is written). Note that the first two `add_file` calls do not specify `dest_path`: in this case, it will be set to the source file's basename (`\"paper.pdf\"` in the first case), so the file will be at the crate's top level when it is written.\n\nWe've started by adding the data entities. Now we need contextual entities to represent Alice and Bob:\n\n```python\nfrom rocrate.model.person import Person\n\nalice_id = \"https://orcid.org/0000-0000-0000-0000\"\nbob_id = \"https://orcid.org/0000-0000-0000-0001\"\nalice = crate.add(Person(crate, alice_id, properties={\n    \"name\": \"Alice Doe\",\n    \"affiliation\": \"University of Flatland\"\n}))\nbob = crate.add(Person(crate, bob_id, properties={\n    \"name\": \"Bob Doe\",\n    \"affiliation\": \"University of Flatland\"\n}))\n```\n\nAt this point, we have a representation of the various entities. Now we need to express the relationships between them. This is done by adding properties that reference other entities:\n\n```python\npaper[\"author\"] = [alice, bob]\ntable[\"author\"] = alice\ndiagram[\"author\"] = bob\n```\n\nYou can also add whole directories together with their contents. In an RO-Crate, a directory is represented by the `Dataset` entity. Create a directory with some placeholder files:\n\n```bash\nmkdir exp/logs\ntouch exp/logs/log1.txt\ntouch exp/logs/log2.txt\n```\n\nNow add it to the crate:\n\n```python\nlogs = crate.add_dataset(\"exp/logs\")\n```\n\nFinally, we serialize the crate to disk:\n\n```python\ncrate.write(\"exp_crate\")\n```\n\nNow the `exp_crate` directory should contain copies of all the files we added and an `ro-crate-metadata.json` file with a [JSON-LD](https://json-ld.org) representation of the entities and relationships we created, with the following layout:\n\n```\nexp_crate/\n|-- images/\n|   `-- figure.svg\n|-- logs/\n|   |-- log1.txt\n|   `-- log2.txt\n|-- paper.pdf\n|-- results.csv\n`-- ro-crate-metadata.json\n```\n\nExploring the `exp_crate` directory, we see that all files and directories contained in `exp/logs` have been added recursively to the crate. However, in the `ro-crate-metadata.json` file, only the top level Dataset with `@id` `\"exp/logs\"` is listed. This is because we used `crate.add_dataset(\"exp/logs\")` rather than adding every file individually. There is no requirement to represent every file and folder within the crate in the `ro-crate-metadata.json` file. If you do want to add files and directories recursively to the metadata, use `crate.add_tree` instead of `crate.add_dataset` (but note that it only works on local directory trees).\n\nSome applications and services support RO-Crates stored as archives. To save the crate in zip format, use `write_zip`:\n\n```python\ncrate.write_zip(\"exp_crate.zip\")\n```\n\n#### Appending elements to property values\n\nWhat ro-crate-py entities actually store is their JSON representation:\n\n```python\npaper.properties()\n```\n\n```json\n{\n  \"@id\": \"paper.pdf\",\n  \"@type\": \"File\",\n  \"name\": \"manuscript\",\n  \"encodingFormat\": \"application/pdf\",\n  \"author\": [\n    {\"@id\": \"https://orcid.org/0000-0000-0000-0000\"},\n    {\"@id\": \"https://orcid.org/0000-0000-0000-0001\"},\n  ]\n}\n```\n\nWhen `paper[\"author\"]` is accessed, a new list containing the `alice` and `bob` entities is generated on the fly. For this reason, calling `append` on `paper[\"author\"]` won't actually modify the `paper` entity in any way. To add an author, use the `append_to` method instead:\n\n```python\ndonald = crate.add(Person(crate, \"https://en.wikipedia.org/wiki/Donald_Duck\", properties={\n  \"name\": \"Donald Duck\"\n}))\npaper.append_to(\"author\", donald)\n```\n\nNote that `append_to` also works if the property to be updated is missing or has only one value:\n\n```python\nfor n in \"Mickey_Mouse\", \"Scrooge_McDuck\":\n    p = crate.add(Person(crate, f\"https://en.wikipedia.org/wiki/{n}\"))\n    donald.append_to(\"follows\", p)\n```\n\n#### Handling of special characters\n\nSince RO-Crate entity identifiers are URIs (relative or absolute), special characters in them must be percent-encoded. When adding data entities from the local file system, this is handled automatically by the library:\n\n```pycon\n\u003e\u003e\u003e crate = ROCrate()\n\u003e\u003e\u003e d = crate.add_dataset(\"read_crate/a b\")\n\u003e\u003e\u003e d.id\n'a%20b/'\n\u003e\u003e\u003e f = crate.add_file(\"read_crate/a b/c d.txt\", dest_path=\"data/a b/c d.txt\")\n\u003e\u003e\u003e f.id\n'data/a%20b/c%20d.txt'\n```\n\nWhen adding an entity whose identifier is an absolute URI (see next section), on the other hand, the identifier provided by the user is expected to be a valid URI, in particular with any special characters in path components already percent-encoded.\n\n#### Adding remote entities\n\nData entities can also be remote:\n\n```python\ninput_data = crate.add_file(\"http://example.org/exp_data.zip\")\n```\n\nBy default the file won't be downloaded, and will be referenced by its URI in the serialized crate:\n\n```json\n{\n  \"@id\": \"http://example.org/exp_data.zip\",\n  \"@type\": \"File\"\n},\n```\n\nIf you add `fetch_remote=True` to the `add_file` call, however, the library (when `crate.write` is called) will try to download the file and include it in the output crate.\n\nAnother option that influences the behavior when dealing with remote entities is `validate_url`, also `False` by default: if it's set to `True`, when the crate is serialized, the library will try to open the URL to add / update metadata bits such as the content's length and format (but it won't try to download the file unless `fetch_remote` is also set).\n\n#### Adding entities with an arbitrary type\n\nAn entity can be of any type listed in the [RO-Crate context](https://www.researchobject.org/ro-crate/1.1/context.jsonld). However, only a few of them have a counterpart (e.g., `File`) in the library's class hierarchy (either because they are very common or because they are associated with specific functionality that can be conveniently embedded in the class implementation). In other cases, you can explicitly pass the type via the `properties` argument:\n\n```python\nfrom rocrate.model.contextentity import ContextEntity\n\nhackathon = crate.add(ContextEntity(crate, \"#bh2021\", properties={\n    \"@type\": \"Hackathon\",\n    \"name\": \"Biohackathon 2021\",\n    \"location\": \"Barcelona, Spain\",\n    \"startDate\": \"2021-11-08\",\n    \"endDate\": \"2021-11-12\"\n}))\n```\n\nNote that entities can have multiple types, e.g.:\n\n```python\n    \"@type\" = [\"File\", \"SoftwareSourceCode\"]\n```\n\n#### Selecting the RO-Crate specification version\n\nBy default, a newly created RO-Crate conforms to the [RO-Crate 1.2](https://w3id.org/ro/crate/1.2) specification, but 1.0 and 1.1 are still supported:\n\n```pycon\n\u003e\u003e\u003e from rocrate.rocrate import ROCrate\n\u003e\u003e\u003e crate = ROCrate()\n\u003e\u003e\u003e crate.version\n'1.2'\n\u003e\u003e\u003e crate = ROCrate(version=\"1.0\")\n\u003e\u003e\u003e crate.version\n'1.0'\n\u003e\u003e\u003e crate.metadata.id\n'ro-crate-metadata.jsonld'\n```\n\nWhen consuming an RO-Crate (see below), the `version` parameter is ignored, and the RO-Crate version is read from the metadata descriptor instead.\n\n\n### Consuming an RO-Crate\n\nAn existing RO-Crate package can be loaded from a directory or zip file:\n\n```python\ncrate = ROCrate('exp_crate')  # or ROCrate('exp_crate.zip')\nfor e in crate.get_entities():\n    print(e.id, e.type)\n```\n\n```\n./ Dataset\nro-crate-metadata.json CreativeWork\npaper.pdf File\nresults.csv File\nimages/figure.svg File\nhttps://orcid.org/0000-0000-0000-0000 Person\nhttps://orcid.org/0000-0000-0000-0001 Person\n```\n\nThe first two entities shown in the output are the [root data entity](https://www.researchobject.org/ro-crate/1.1/root-data-entity.html) and the [metadata file descriptor](https://www.researchobject.org/ro-crate/1.1/metadata.html), respectively. The former represents the whole crate, while the latter represents the metadata file. These are special entities managed by the `ROCrate` object, and are always present. The other entities are the ones we added in the [section on RO-Crate creation](#creating-an-ro-crate).\n\nAs shown above, `get_entities` allows to iterate over all entities in the crate. You can also access only data entities with `crate.data_entities` and only contextual entities with `crate.contextual_entities`. For instance:\n\n```python\nfor e in crate.data_entities:\n    author = e.get(\"author\")\n    if not author:\n        continue\n    elif isinstance(author, list):\n        print(e.id, [p[\"name\"] for p in author])\n    else:\n        print(e.id, repr(author[\"name\"]))\n```\n\n```\npaper.pdf ['Alice Doe', 'Bob Doe']\nresults.csv 'Alice Doe'\nimages/figure.svg 'Bob Doe'\n```\n\nYou can fetch an entity by its `@id` as follows:\n\n```python\narticle = crate.dereference(\"paper.pdf\")\n```\n\n## Advanced features\n\n### Detached crates\n\n[RO-Crate 1.2](https://www.researchobject.org/ro-crate/whats-changed-in-1-2) introduces the concept of _detached_ RO-Crates, which have no defined root directory: in detached crates, the metadata is accessed independently, for instance via an API or from a standalone metadata file. By contrast, \"traditional\" crates that describe a payload of files and directories contained in a root directory are called _attached_.\n\nBoth detached and attached crates can have a root data entity with an absolute URI as `@id`. To create an RO-Crate whose root data entity `@id` is different from the default `./`, use the `root_dataset_id` argument in the constructor:\n\n```python\nfrom rocrate.rocrate import ROCrate\n\nurl = \"http://example.com/crate/\"\ncrate = ROCrate(root_dataset_id=url)\n```\n\nIn detached crates, _all_ data entities must be web-based, i.e., have an absolute URI as `@id`:\n\n```python\nfile_1 = crate.add_file(f\"{url}file_1\")  # http://example.com/crate/file_1\n```\n\nThe [recommended way](https://www.researchobject.org/ro-crate/specification/1.2/structure.html#types-of-ro-crate) to store a detached crate on disk is to write a single metadata file called `${prefix}-ro-crate-metadata.json`, where `${prefix}` is a variable. The library supports this through the `write_detached` method, which takes as argument an arbitrary path (a warning will be issued if the path does not follow the above pattern):\n\n```python\ncrate.write_detached(\"/tmp/example-ro-crate-metadata.json\")\n```\n\nOne of the ways to consume a detached crate is to read the metadata from a local file. For instance, to read the crate that we just wrote:\n\n```python\nread_crate = ROCrate(\"/tmp/example-ro-crate-metadata.json\")\nread_file_1 = read_crate.dereference(f\"{url}file_1\")\n```\n\nThis also works with a local `file://` URI:\n\n```python\nread_crate = ROCrate(\"file:///tmp/example-ro-crate-metadata.json\")\n```\n\nand with a remote URI:\n\n```python\nbase = \"https://raw.githubusercontent.com/ResearchObject/ro-crate-py/master/test/test-data/\"\nread_crate = ROCrate(f\"{base}detached-ro-crate-metadata.json\")\nassert read_crate.root_dataset.id == base\nsample_file = read_crate.dereference(f\"{base}sample_file.txt\")\ntest_file_galaxy = read_crate.dereference(f\"{base}test_file_galaxy.txt\")\n```\n\nAnother way to read a detached crate is to pass a JSON dictionary with the RO-Crate metadata directly to `ROCrate`. For instance:\n\n```python\nimport json\nfrom rocrate.rocrate import ROCrate\n\nwith open(\"/tmp/example-ro-crate-metadata.json\") as f:\n    metadata = json.load(f)\ncrate = ROCrate(metadata)\n```\n\nIn the above example we read the metadata from a local file, but you could get it from an API endpoint or any other source.\n\n\n### Subcrates\n\nAn RO-Crate can contain one or more nested RO-Crates. For instance, consider the following layout:\n\n```\ncrate_with_subcrates/\n|-- file.txt\n|-- ro-crate-metadata.json\n|-- subcrate\n|   |-- ro-crate-metadata.json\n|   |-- subfile.txt\n|   `-- subsubcrate\n|       |-- deepfile.txt\n|       `-- ro-crate-metadata.json\n`-- subcrate2\n    |-- ro-crate-metadata.json\n    `-- subfile.txt\n```\n\nIn the JSON-LD metadata, the presence of a nested crate rooted at a given directory is indicated by a `conformsTo` pointing to the generic RO-Crate profile `https://w3id.org/ro/crate` (see [Referencing other RO-Crates](https://www.researchobject.org/ro-crate/specification/1.2/data-entities.html#referencing-other-ro-crates)):\n\n```json\n{\n    \"@id\": \"subcrate/\",\n    \"@type\": \"Dataset\",\n    \"conformsTo\": \"https://w3id.org/ro/crate\"\n}\n```\n\nSince nested crates can potentially contain many and / or large files, they are not loaded by default: to enable their loading, pass `load_subcrates=True` to the `RO-Crate` initializer:\n\n```pycon\n\u003e\u003e\u003e from rocrate.rocrate import ROCrate\n\u003e\u003e\u003e crate = ROCrate(\"test/test-data/crate_with_subcrates\", load_subcrates=True)\n\u003e\u003e\u003e crate.subcrate_entities\n[\u003csubcrate/ Dataset\u003e, \u003csubcrate2/ Dataset\u003e]\n```\n\nAt this point, the nested crates have not been loaded yet. You can load a nested crate explicitly:\n\n```pycon\n\u003e\u003e\u003e nested_crate = subcrate.get_crate()\n\u003e\u003e\u003e nested_crate.data_entities\n[\u003csubfile.txt File\u003e, \u003csubsubcrate/ Dataset\u003e]\n\u003e\u003e\u003e nested_crate.subcrate_entities\n[\u003csubsubcrate/ Dataset\u003e]\n```\n\nAlternatively, you can dereference an item from the higher level crate:\n\n```pycon\n\u003e\u003e\u003e crate.dereference(\"subcrate2/subfile.txt\")\n\u003csubfile.txt File\u003e\n```\n\nUp to this point, we have seen how to consume an existing RO-Crate. The following example shows how to create a new one:\n\n```pycon\n\u003e\u003e\u003e crate = ROCrate()\n\u003e\u003e\u003e crate.add_file(\"test/test-data/test_file_galaxy.txt\")\n\u003ctest_file_galaxy.txt File\u003e\n\u003e\u003e\u003e subcrate = crate.add_subcrate(dest_path=\"subcrate/\")\n\u003e\u003e\u003e subcrate\n\u003csubcrate/ Dataset\u003e\n\u003e\u003e\u003e assert subcrate.get(\"conformsTo\") == \"https://w3id.org/ro/crate\"\n\u003e\u003e\u003e assert crate.subcrate_entities == [subcrate]\n\u003e\u003e\u003e subcrate_crate = subcrate.get_crate()\n\u003e\u003e\u003e subcrate_crate\n\u003crocrate.rocrate.ROCrate object at 0x7e4b79adfad0\u003e\n\u003e\u003e\u003e subsubcrate = subcrate_crate.add_subcrate(dest_path=\"subsubcrate/\")\n\u003e\u003e\u003e assert subcrate_crate.subcrate_entities == [subsubcrate]\n\u003e\u003e\u003e subsubcrate_crate = subsubcrate.get_crate()\n\u003e\u003e\u003e subsubf = subsubcrate_crate.add_file(\"setup.cfg\")\n\u003e\u003e\u003e assert crate.dereference(\"subcrate/subsubcrate/setup.cfg\") is subsubf\n\u003e\u003e\u003e crate.write(\"/tmp/crate_with_subcrates\")\n```\n\n### Modifying the crate from JSON-LD dictionaries\n\nThe `add_jsonld` method allows to add a contextual entity directly from a\nJSON-LD dictionary containing at least the `@id` and `@type` keys:\n\n```python\ncrate.add_jsonld({\n    \"@id\": \"https://orcid.org/0000-0000-0000-0000\",\n    \"@type\": \"Person\",\n    \"name\": \"Alice Doe\"\n})\n```\n\nExisting entities can be updated from JSON-LD dictionaries via `update_jsonld`:\n\n```python\ncrate.update_jsonld({\n    \"@id\": \"https://orcid.org/0000-0000-0000-0000\",\n    \"name\": \"Alice K. Doe\"\n})\n```\n\nThere is also an `add_or_update_jsonld` method that adds the entity if it's\nnot already in the crate and updates it if it already exists (note that, when\nupdating, the `@type` key is ignored). This allows to \"patch\" an RO-Crate from\na JSON-LD file. For instance, suppose you have the following `patch.json` file:\n\n```json\n{\n    \"@graph\": [\n        {\n            \"@id\": \"./\",\n            \"author\": {\"@id\": \"https://orcid.org/0000-0000-0000-0001\"}\n        },\n        {\n            \"@id\": \"https://orcid.org/0000-0000-0000-0001\",\n            \"@type\": \"Person\",\n            \"name\": \"Bob Doe\"\n        }\n    ]\n}\n```\n\nThen the following sets Bob as the author of the crate according to the above\nfile:\n\n```python\ncrate = ROCrate(\"temp-crate\")\nwith open(\"patch.json\") as f:\n    json_data = json.load(f)\nfor d in json_data.get(\"@graph\", []):\n    crate.add_or_update_jsonld(d)\n```\n\n## Command Line Interface\n\n`ro-crate-py` includes a hierarchical command line interface: the `rocrate` tool. `rocrate` is the top-level command, while specific functionalities are provided via sub-commands. Currently, the tool allows to initialize a directory tree as an RO-Crate (`rocrate init`) and to modify the metadata of an existing RO-Crate (`rocrate add`).\n\n```console\n$ rocrate --help\nUsage: rocrate [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  add\n  init\n  write-zip\n```\n\n### Crate initialization\n\nThe `rocrate init` command explores a directory tree and generates an RO-Crate metadata file (`ro-crate-metadata.json`) listing all files and directories as `File` and `Dataset` entities, respectively.\n\n```console\n$ rocrate init --help\nUsage: rocrate init [OPTIONS]\n\nOptions:\n  --gen-preview         Generate a HTML preview file for the crate.\n  -e, --exclude NAME    Exclude files or directories from the metadata file.\n                        NAME may be a single name or a comma-separated list of\n                        names.\n  -c, --crate-dir PATH  The path to the root data entity of the crate.\n                        Defaults to the current working directory.\n  --help                Show this message and exit.\n```\n\nThe command acts on the current directory, unless the `-c` option is specified. The metadata file is added (overwritten if present) to the directory at the top level, turning it into an RO-Crate.\n\n### Adding items to the crate\n\nThe `rocrate add` command allows to add file, datasets (directories), workflows and other entity types (currently [testing-related metadata](https://crs4.github.io/life_monitor/workflow_testing_ro_crate)) to an RO-Crate:\n\n```console\n$ rocrate add --help\nUsage: rocrate add [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  dataset\n  file\n  test-definition\n  test-instance\n  test-suite\n  workflow\n```\n\nNote that data entities (e.g., workflows) must already be present in the directory tree: the effect of the command is to register them in the metadata file.\n\n### Example\n\n```bash\n# From the ro-crate-py repository root\ncd test/test-data/ro-crate-galaxy-sortchangecase\n```\n\nThis directory is already an RO-Crate. Delete the metadata file to get a plain directory tree:\n\n```bash\nrm ro-crate-metadata.json\n```\n\nNow the directory tree contains several files and directories, including a Galaxy workflow and a Planemo test file, but it's not an RO-Crate since there is no metadata file. Initialize the crate:\n\n```bash\nrocrate init\n```\n\nThis creates an `ro-crate-metadata.json` file that lists files and directories rooted at the current directory. Note that the Galaxy workflow is listed as a plain `File`:\n\n```json\n{\n  \"@id\": \"sort-and-change-case.ga\",\n  \"@type\": \"File\"\n}\n```\n\nTo register the workflow as a `ComputationalWorkflow`:\n\n```bash\nrocrate add workflow -l galaxy sort-and-change-case.ga\n```\n\nNow the workflow has a type of `[\"File\", \"SoftwareSourceCode\", \"ComputationalWorkflow\"]` and points to a `ComputerLanguage` entity that represents the Galaxy workflow language. Also, the workflow is listed as the crate's `mainEntity` (this is required by the [Workflow RO-Crate profile](https://w3id.org/workflowhub/workflow-ro-crate/1.0), a subtype of RO-Crate which provides extra specifications for workflow metadata).\n\nTo add [workflow testing metadata](https://crs4.github.io/life_monitor/workflow_testing_ro_crate) to the crate:\n\n```bash\nrocrate add test-suite -i '#test1'\nrocrate add test-instance '#test1' http://example.com -r jobs -i '#test1_1'\nrocrate add test-definition '#test1' test/test1/sort-and-change-case-test.yml -e planemo -v '\u003e=0.70'\n```\n\nTo add files or directories after crate initialization:\n\n```bash\ncp ../sample_file.txt .\nrocrate add file sample_file.txt -P name=sample -P description=\"Sample file\"\ncp -r ../test_add_dir .\nrocrate add dataset test_add_dir\n```\n\nThe above example also shows how to set arbitrary properties for the entity with `-P`. This is supported by most `rocrate add` subcommands.\n\n```console\n$ rocrate add workflow --help\nUsage: rocrate add workflow [OPTIONS] PATH\n\nOptions:\n  -l, --language [cwl|galaxy|knime|nextflow|snakemake|compss|autosubmit]\n                                  The workflow language.\n  -c, --crate-dir PATH            The path to the root data entity of the\n                                  crate. Defaults to the current working\n                                  directory.\n  -P, --property KEY=VALUE        Add an additional property to the metadata\n                                  for this entity. Can be used multiple times\n                                  to set multiple properties.\n  --help                          Show this message and exit.\n```\n\n## License\n\n* Copyright 2019-2026 The University of Manchester, UK\n* Copyright 2020-2026 Vlaams Instituut voor Biotechnologie (VIB), BE\n* Copyright 2020-2026 Barcelona Supercomputing Center (BSC), ES\n* Copyright 2020-2026 Center for Advanced Studies, Research and Development in Sardinia (CRS4), IT\n* Copyright 2022-2026 École Polytechnique Fédérale de Lausanne, CH\n* Copyright 2024-2026 Data Centre, SciLifeLab, SE\n* Copyright 2024-2026 National Institute of Informatics (NII), JP\n* Copyright 2025-2026 Senckenberg Society for Nature Research (SGN), DE\n* Copyright 2025-2026 European Molecular Biology Laboratory (EMBL), Heidelberg, DE\n* Copyright 2026 Spanish National Research Council (CSIC), ES\n\nLicensed under the\nApache License, version 2.0 \u003chttps://www.apache.org/licenses/LICENSE-2.0\u003e,\nsee the file `LICENSE.txt` for details.\n\n## Cite as\n\n[![DOI](https://zenodo.org/badge/216605684.svg)](https://zenodo.org/badge/latestdoi/216605684)\n\nThe above DOI corresponds to the latest versioned release as [published to Zenodo](https://zenodo.org/record/3956493), where you will find all earlier releases.\nTo cite `ro-crate-py` independent of version, use \u003chttps://doi.org/10.5281/zenodo.3956493\u003e, which will always redirect to the latest release.\n\nYou may also be interested in the paper [Packaging research artefacts with RO-Crate](https://doi.org/10.3233/DS-210053).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresearchobject%2Fro-crate-py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fresearchobject%2Fro-crate-py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresearchobject%2Fro-crate-py/lists"}