diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 936525e..2069e54 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -19,7 +19,7 @@ jobs: strategy: matrix: os: ['ubuntu-latest', 'macos-latest', 'windows-latest'] - python-version: ['3.7', '3.8', '3.9', '3.10'] + python-version: ['3.8', '3.9', '3.10', '3.11'] runs-on: ${{ matrix.os }} diff --git a/rocrate/metadata.py b/rocrate/metadata.py index 2cbce35..4e15571 100644 --- a/rocrate/metadata.py +++ b/rocrate/metadata.py @@ -30,8 +30,11 @@ def read_metadata(metadata_path): Return a tuple of two elements: the context; a dictionary that maps entity ids to the entities themselves. """ - with open(metadata_path) as f: - metadata = json.load(f) + if isinstance(metadata_path, dict): + metadata = metadata_path + else: + with open(metadata_path) as f: + metadata = json.load(f) try: context = metadata['@context'] graph = metadata['@graph'] diff --git a/rocrate/rocrate.py b/rocrate/rocrate.py index 6b622b8..827c913 100644 --- a/rocrate/rocrate.py +++ b/rocrate/rocrate.py @@ -113,26 +113,31 @@ def __init_from_tree(self, top_dir, gen_preview=False): self.add(Preview(self, source)) def __read(self, source, gen_preview=False): - source = Path(source) - if not source.exists(): - raise FileNotFoundError(errno.ENOENT, f"'{source}' not found") - if zipfile.is_zipfile(source): - zip_path = tempfile.mkdtemp(prefix="rocrate_") - atexit.register(shutil.rmtree, zip_path) - with zipfile.ZipFile(source, "r") as zf: - zf.extractall(zip_path) - source = Path(zip_path) - metadata_path = source / Metadata.BASENAME - if not metadata_path.is_file(): - metadata_path = source / LegacyMetadata.BASENAME - if not metadata_path.is_file(): - raise ValueError(f"Not a valid RO-Crate: missing {Metadata.BASENAME}") + if isinstance(source, dict): + metadata_path = source + else: + source = Path(source) + if not source.exists(): + raise FileNotFoundError(errno.ENOENT, f"'{source}' not found") + if zipfile.is_zipfile(source): + zip_path = tempfile.mkdtemp(prefix="rocrate_") + atexit.register(shutil.rmtree, zip_path) + with zipfile.ZipFile(source, "r") as zf: + zf.extractall(zip_path) + source = Path(zip_path) + metadata_path = source / Metadata.BASENAME + if not metadata_path.is_file(): + metadata_path = source / LegacyMetadata.BASENAME + if not metadata_path.is_file(): + raise ValueError(f"Not a valid RO-Crate: missing {Metadata.BASENAME}") _, entities = read_metadata(metadata_path) self.__read_data_entities(entities, source, gen_preview) self.__read_contextual_entities(entities) return source def __read_data_entities(self, entities, source, gen_preview): + if isinstance(source, dict): + source = Path("") metadata_id, root_id = find_root_entity_id(entities) root_entity = entities.pop(root_id) assert root_id == root_entity.pop('@id') @@ -447,7 +452,7 @@ def _copy_unlisted(self, top, base_path): def write(self, base_path): base_path = Path(base_path) base_path.mkdir(parents=True, exist_ok=True) - if self.source: + if self.source and not isinstance(self.source, dict): self._copy_unlisted(self.source, base_path) for writable_entity in self.data_entities + self.default_entities: writable_entity.write(base_path) diff --git a/test/test_read.py b/test/test_read.py index 48be710..a116451 100644 --- a/test/test_read.py +++ b/test/test_read.py @@ -535,3 +535,61 @@ def test_indirect_data_entity(tmpdir): d2_e = crate.dereference("d1/d2") assert d2_e assert d2_e in crate.data_entities + + +@pytest.mark.filterwarnings("ignore") +def test_from_dict(tmpdir): + metadata = { + "@context": "https://w3id.org/ro/crate/1.1/context", + "@graph": [ + { + "@id": "ro-crate-metadata.json", + "@type": "CreativeWork", + "about": {"@id": "./"}, + "conformsTo": {"@id": "https://w3id.org/ro/crate/1.1"} + }, + { + "@id": "./", + "@type": "Dataset", + "creator": {"@id": "#josiah"}, + "hasPart": {"@id": "d1"} + }, + { + "@id": "d1", + "@type": "Dataset", + "hasPart": {"@id": "d1/d2"} + }, + { + "@id": "d1/d2", + "@type": "Dataset", + "hasPart": {"@id": "d1/d2/f1"} + }, + { + "@id": "d1/d2/f1", + "@type": "File" + }, + { + "@id": "#josiah", + "@type": "Person", + 'name': 'Josiah Carberry' + }, + ] + } + crate = ROCrate(metadata) + d1 = crate.dereference("d1") + assert d1 + d2 = crate.dereference("d1/d2") + assert d2 + f1 = crate.dereference("d1/d2/f1") + assert f1 + p = crate.dereference("#josiah") + assert p + assert set(crate.data_entities) == {d1, d2, f1} + assert set(crate.contextual_entities) == {p} + out_path = tmpdir / 'out_crate' + with pytest.raises(OSError): + crate.write(out_path) + # make it writable + for entity in d1, d2, f1: + entity.source = None + crate.write(out_path)