Skip to content

Commit 6e796cb

Browse files
authored
Merge pull request #354 from linkml/issue-2458
add ability to call json_loader with a Path object
2 parents b9f7d5e + f1625ad commit 6e796cb

File tree

4 files changed

+36
-7
lines changed

4 files changed

+36
-7
lines changed

linkml_runtime/loaders/json_loader.py

+25-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import logging
3-
from typing import Union, TextIO, Optional, Dict, Type, List
3+
from pathlib import Path
4+
from typing import Union, TextIO, Optional, Type, List
45

56
from hbreader import FileInfo
67

@@ -14,7 +15,7 @@
1415
class JSONLoader(Loader):
1516

1617
def load_as_dict(self,
17-
source: Union[str, dict, TextIO],
18+
source: Union[str, dict, TextIO],
1819
*,
1920
base_dir: Optional[str] = None,
2021
metadata: Optional[FileInfo] = None) -> Union[dict, List[dict]]:
@@ -23,12 +24,33 @@ def load_as_dict(self,
2324
return self.json_clean(data_as_dict)
2425

2526
def load_any(self,
26-
source: Union[str, dict, TextIO],
27+
source: Union[str, dict, TextIO, Path],
2728
target_class: Type[Union[BaseModel, YAMLRoot]],
2829
*,
2930
base_dir: Optional[str] = None,
3031
metadata: Optional[FileInfo] = None,
3132
**_) -> Union[BaseModel, YAMLRoot, List[BaseModel], List[YAMLRoot]]:
33+
"""
34+
Load the JSON in source into the python target_class structure
35+
36+
:param source: JSON data source. Can be a URL, a file name, a JSON string, a resolveable filepath or an existing graph
37+
:param target_class: LinkML class to load the JSON into
38+
:param base_dir: Base directory that can be used if file name or URL. This is copied into metadata if present
39+
:param metadata: source information. Used by some loaders to record where information came from
40+
:return: data instances of target_class
41+
"""
42+
43+
# see https://github.com/linkml/linkml/issues/2458, this works around a limitation in hbreader
44+
if isinstance(source, Path):
45+
full_path = source.resolve()
46+
try:
47+
with full_path.open("r", encoding="utf-8") as file:
48+
new_source = json.load(file)
49+
source = new_source
50+
except FileNotFoundError as e:
51+
raise ValueError(f"File not found: {source}") from e
52+
except json.JSONDecodeError as e:
53+
raise ValueError(f"Invalid JSON in {source}: {e}")
3254
data_as_dict = self.load_as_dict(source, base_dir=base_dir, metadata=metadata)
3355

3456
if isinstance(data_as_dict, dict):

linkml_runtime/loaders/loader_root.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from abc import ABC, abstractmethod
2+
from pathlib import Path
23
from typing import TextIO, Union, Optional, Callable, Dict, Type, Any, List
34
from logging import getLogger
45

@@ -83,7 +84,7 @@ def load_as_dict(self, *args, **kwargs) -> Union[dict, List[dict]]:
8384
raise NotImplementedError()
8485

8586
@abstractmethod
86-
def load_any(self, source: Union[str, dict, TextIO], target_class: Type[Union[BaseModel, YAMLRoot]], *, base_dir: Optional[str] = None,
87+
def load_any(self, source: Union[str, dict, TextIO, Path], target_class: Type[Union[BaseModel, YAMLRoot]], *, base_dir: Optional[str] = None,
8788
metadata: Optional[FileInfo] = None, **_) -> Union[BaseModel, YAMLRoot, List[BaseModel], List[YAMLRoot]]:
8889
"""
8990
Load source as an instance of target_class, or list of instances of target_class

linkml_runtime/loaders/rdf_loader.py

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
from typing import Union, TextIO, Optional, Type, List
2-
32
from hbreader import FileInfo
4-
53
from linkml_runtime.loaders.loader_root import Loader
64
from linkml_runtime.utils.context_utils import CONTEXTS_PARAM_TYPE
75
from linkml_runtime.utils.yamlutils import YAMLRoot

tests/test_loaders_dumpers/test_loaders.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
import unittest
33
from typing import Union, TextIO, Type, Optional
4-
4+
from pathlib import Path
55
from hbreader import FileInfo
66

77
from linkml_runtime.loaders import yaml_loader, json_loader, rdf_loader, RDFLoader
@@ -25,6 +25,14 @@ def test_yaml_loader(self):
2525
""" Load obo_sample.yaml, emit obo_sample_yaml.yaml and compare to obo_sample_output.yaml """
2626
self.loader_test('obo_sample.yaml', Package, yaml_loader)
2727

28+
def test_json_loader_path(self):
29+
""" Load obo_sample.json, emit obo_sample_json.yaml and check the results """
30+
REPO_ROOT = Path(__file__).parent.parent.parent
31+
path = REPO_ROOT / "tests" / "test_loaders_dumpers" / "input" / "obo_sample.json"
32+
data = json_loader.load(Path(path), Package, base_dir=self.env.indir)
33+
assert isinstance(data, Package)
34+
assert "system" in data
35+
2836
def test_json_loader(self):
2937
""" Load obo_sample.json, emit obo_sample_json.yaml and check the results """
3038
self.loader_test('obo_sample.json', Package, json_loader)

0 commit comments

Comments
 (0)