Skip to content

Commit c38bfae

Browse files
Merge pull request #418 from linkml/typing_additions
Adding __future__type annotations to yaml_loader, loader_root.
2 parents b0578fe + bf998d8 commit c38bfae

File tree

3 files changed

+83
-83
lines changed

3 files changed

+83
-83
lines changed

linkml_runtime/__init__.py

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,18 @@
3131

3232
MAIN_SCHEMA_PATH = SCHEMA_DIRECTORY / "meta.yaml"
3333

34-
LINKML_ANNOTATIONS = SCHEMA_DIRECTORY / "annotations.yaml"
35-
LINKML_ARRAY = SCHEMA_DIRECTORY / "array.yaml"
36-
LINKML_EXTENSIONS = SCHEMA_DIRECTORY / "extensions.yaml"
37-
LINKML_MAPPINGS = SCHEMA_DIRECTORY / "mappings.yaml"
38-
LINKML_TYPES = SCHEMA_DIRECTORY / "types.yaml"
39-
LINKML_UNITS = SCHEMA_DIRECTORY / "units.yaml"
40-
LINKML_VALIDATION = SCHEMA_DIRECTORY / "validation.yaml"
41-
42-
43-
URI_TO_LOCAL = {
44-
"https://w3id.org/linkml/annotations.yaml": str(LINKML_ANNOTATIONS),
45-
"https://w3id.org/linkml/array.yaml": str(LINKML_ARRAY),
46-
"https://w3id.org/linkml/extensions.yaml": str(LINKML_EXTENSIONS),
47-
"https://w3id.org/linkml/mappings.yaml": str(LINKML_MAPPINGS),
48-
"https://w3id.org/linkml/meta.yaml": str(MAIN_SCHEMA_PATH),
49-
"https://w3id.org/linkml/types.yaml": str(LINKML_TYPES),
50-
"https://w3id.org/linkml/units.yaml": str(LINKML_UNITS),
51-
"https://w3id.org/linkml/validation.yaml": str(LINKML_VALIDATION),
52-
}
34+
LINKML_COMPONENTS = ["annotations", "array", "extensions", "mappings", "meta", "types", "units", "validation"]
35+
36+
# map component names to their schema paths as Path objects
37+
# file paths are of the form SCHEMA_DIRECTORY / "{component}.yaml"
38+
LINKML_PATHS = {c: SCHEMA_DIRECTORY / f"{c}.yaml" for c in LINKML_COMPONENTS}
39+
40+
# map linkml URIs to their local paths as Path objects
41+
# URIs are of the form "https://w3id.org/linkml/{component}.yaml"
42+
URI_TO_PATH = {f"https://w3id.org/linkml/{c}.yaml": str(LINKML_PATHS[c]) for c in LINKML_COMPONENTS}
43+
44+
# map linkml URIs to their local paths in string form
45+
URI_TO_LOCAL = {key: str(path) for key, path in URI_TO_PATH.items()}
5346

5447

5548
class MappingError(ValueError):

linkml_runtime/loaders/loader_root.py

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
from __future__ import annotations
2+
13
from abc import ABC, abstractmethod
24
from logging import getLogger
3-
from pathlib import Path
4-
from typing import Any, Callable, Optional, TextIO, Union
5+
from typing import TYPE_CHECKING, Any, TextIO
56

67
from hbreader import FileInfo, hbread
78
from jsonasobj2 import JsonObj, as_dict
@@ -10,6 +11,10 @@
1011
from linkml_runtime import URI_TO_LOCAL
1112
from linkml_runtime.utils.yamlutils import YAMLRoot
1213

14+
if TYPE_CHECKING:
15+
from collections.abc import Callable
16+
from pathlib import Path
17+
1318
CACHE_SIZE = 1024
1419

1520

@@ -41,12 +46,12 @@ def _is_empty(o) -> bool:
4146

4247
def load_source(
4348
self,
44-
source: Union[str, dict, TextIO],
45-
loader: Callable[[Union[str, dict], FileInfo], Optional[Union[dict, list]]],
46-
target_class: Union[type[YAMLRoot], type[BaseModel]],
47-
accept_header: Optional[str] = "text/plain, application/yaml;q=0.9",
48-
metadata: Optional[FileInfo] = None,
49-
) -> Optional[Union[BaseModel, YAMLRoot, list[BaseModel], list[YAMLRoot]]]:
49+
source: str | dict | TextIO,
50+
loader: Callable[[str | dict, FileInfo], dict | list | None],
51+
target_class: type[YAMLRoot | BaseModel],
52+
accept_header: str | None = "text/plain, application/yaml;q=0.9",
53+
metadata: FileInfo | None = None,
54+
) -> BaseModel | YAMLRoot | list[BaseModel] | list[YAMLRoot] | None:
5055
"""Base loader - convert a file, url, string, open file handle or dictionary into an instance
5156
of target_class
5257
@@ -63,7 +68,7 @@ def load_source(
6368
data_as_dict = loader(data, metadata)
6469
return self._construct_target_class(data_as_dict, target_class=target_class)
6570

66-
def load(self, *args, **kwargs) -> Union[BaseModel, YAMLRoot]:
71+
def load(self, *args, **kwargs) -> BaseModel | YAMLRoot:
6772
"""
6873
Load source as an instance of target_class
6974
@@ -77,22 +82,22 @@ def load(self, *args, **kwargs) -> Union[BaseModel, YAMLRoot]:
7782
results = self.load_any(*args, **kwargs)
7883
if isinstance(results, (BaseModel, YAMLRoot)):
7984
return results
80-
else:
81-
raise ValueError(f"Result is not an instance of BaseModel or YAMLRoot: {type(results)}")
85+
msg = f"Result is not an instance of BaseModel or YAMLRoot: {type(results)}"
86+
raise ValueError(msg)
8287

83-
def load_as_dict(self, *args, **kwargs) -> Union[dict, list[dict]]:
88+
def load_as_dict(self, *args, **kwargs) -> dict | list[dict]:
8489
raise NotImplementedError()
8590

8691
@abstractmethod
8792
def load_any(
8893
self,
89-
source: Union[str, dict, TextIO, Path],
90-
target_class: type[Union[BaseModel, YAMLRoot]],
94+
source: str | dict | TextIO | Path,
95+
target_class: type[BaseModel | YAMLRoot],
9196
*,
92-
base_dir: Optional[str] = None,
93-
metadata: Optional[FileInfo] = None,
97+
base_dir: str | None = None,
98+
metadata: FileInfo | None = None,
9499
**_,
95-
) -> Union[BaseModel, YAMLRoot, list[BaseModel], list[YAMLRoot]]:
100+
) -> BaseModel | YAMLRoot | list[BaseModel] | list[YAMLRoot]:
96101
"""
97102
Load source as an instance of target_class, or list of instances of target_class
98103
@@ -106,8 +111,8 @@ def load_any(
106111
raise NotImplementedError()
107112

108113
def loads_any(
109-
self, source: str, target_class: type[Union[BaseModel, YAMLRoot]], *, metadata: Optional[FileInfo] = None, **_
110-
) -> Union[BaseModel, YAMLRoot, list[BaseModel], list[YAMLRoot]]:
114+
self, source: str, target_class: type[BaseModel | YAMLRoot], *, metadata: FileInfo | None = None, **_
115+
) -> BaseModel | YAMLRoot | list[BaseModel] | list[YAMLRoot]:
111116
"""
112117
Load source as a string as an instance of target_class, or list of instances of target_class
113118
@param source: source
@@ -119,8 +124,8 @@ def loads_any(
119124
return self.load_any(source, target_class, metadata=metadata)
120125

121126
def loads(
122-
self, source: str, target_class: type[Union[BaseModel, YAMLRoot]], *, metadata: Optional[FileInfo] = None, **_
123-
) -> Union[BaseModel, YAMLRoot]:
127+
self, source: str, target_class: type[BaseModel | YAMLRoot], *, metadata: FileInfo | None = None, **_
128+
) -> BaseModel | YAMLRoot:
124129
"""
125130
Load source as a string
126131
:param source: source
@@ -132,53 +137,52 @@ def loads(
132137
return self.load(source, target_class, metadata=metadata)
133138

134139
def _construct_target_class(
135-
self, data_as_dict: Union[dict, list[dict]], target_class: Union[type[YAMLRoot], type[BaseModel]]
136-
) -> Optional[Union[BaseModel, YAMLRoot, list[BaseModel], list[YAMLRoot]]]:
140+
self, data_as_dict: dict | list[dict], target_class: type[YAMLRoot | BaseModel]
141+
) -> BaseModel | YAMLRoot | list[BaseModel] | list[YAMLRoot] | None:
137142
if data_as_dict:
138143
if isinstance(data_as_dict, list):
139144
if issubclass(target_class, YAMLRoot):
140145
return [target_class(**as_dict(x)) for x in data_as_dict]
141-
elif issubclass(target_class, BaseModel):
146+
if issubclass(target_class, BaseModel):
142147
return [target_class.model_validate(as_dict(x)) for x in data_as_dict]
143-
else:
144-
raise ValueError(f"Cannot load list of {target_class}")
145-
elif isinstance(data_as_dict, dict):
148+
msg = f"Cannot load list of {target_class}"
149+
raise ValueError(msg)
150+
if isinstance(data_as_dict, dict):
146151
if issubclass(target_class, BaseModel):
147152
return target_class.model_validate(data_as_dict)
148153
return target_class(**data_as_dict)
149154

150155
if isinstance(data_as_dict, JsonObj):
151156
return [target_class(**as_dict(x)) for x in data_as_dict]
152-
else:
153-
raise ValueError(f"Unexpected type {data_as_dict}")
154-
else:
155-
return None
157+
158+
msg = f"Unexpected type {data_as_dict}"
159+
# should really be a TypeError
160+
raise ValueError(msg)
161+
return None
156162

157163
def _read_source(
158164
self,
159-
source: Union[str, dict, TextIO],
165+
source: str | dict | TextIO,
160166
*,
161-
base_dir: Optional[str] = None,
162-
metadata: Optional[FileInfo] = None,
163-
accept_header: Optional[str] = "text/plain, application/yaml;q=0.9",
164-
) -> Union[dict, str]:
167+
base_dir: str | None = None,
168+
metadata: FileInfo | None = None,
169+
accept_header: str | None = "text/plain, application/yaml;q=0.9",
170+
) -> dict | str:
165171
if metadata is None:
166172
metadata = FileInfo()
167173
if base_dir and not metadata.base_path:
168174
metadata.base_path = base_dir
169175

170-
if not isinstance(source, dict):
171-
# Try to get local version of schema, if one is known to exist
172-
try:
173-
if str(source) in URI_TO_LOCAL:
174-
source = str(URI_TO_LOCAL[str(source)])
175-
except (TypeError, KeyError) as e:
176-
# Fine, use original `source` value
177-
logger = getLogger("linkml_runtime.loaders.Loader")
178-
logger.debug(f"Error converting stringlike source to local linkml file: {source}, got: {e}")
179-
180-
data = hbread(source, metadata, base_dir, accept_header)
181-
else:
182-
data = source
183-
184-
return data
176+
if isinstance(source, dict):
177+
return source
178+
179+
# Try to get local version of schema, if one is known to exist
180+
try:
181+
if str(source) in URI_TO_LOCAL:
182+
source = str(URI_TO_LOCAL[str(source)])
183+
except (TypeError, KeyError) as e:
184+
# Fine, use original `source` value
185+
logger = getLogger("linkml_runtime.loaders.Loader")
186+
logger.debug(f"Error converting stringlike source to local linkml file: {source}, got: {e}")
187+
188+
return hbread(source, metadata, base_dir, accept_header)

linkml_runtime/loaders/yaml_loader.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
1+
from __future__ import annotations
2+
13
import os
24
from io import StringIO
3-
from typing import Optional, TextIO, Union
5+
from typing import TYPE_CHECKING, TextIO
46

57
import yaml
68
from hbreader import FileInfo
7-
from pydantic import BaseModel
89

910
from linkml_runtime.loaders.loader_root import Loader
1011
from linkml_runtime.utils.yamlutils import DupCheckYamlLoader, YAMLRoot
1112

13+
if TYPE_CHECKING:
14+
from pydantic import BaseModel
15+
1216

1317
class YAMLLoader(Loader):
1418
"""
1519
A Loader that is capable of instantiating LinkML data objects from a YAML file
1620
"""
1721

1822
def load_as_dict(
19-
self, source: Union[str, dict, TextIO], *, base_dir: Optional[str] = None, metadata: Optional[FileInfo] = None
20-
) -> Union[dict, list[dict]]:
23+
self, source: str | dict | TextIO, *, base_dir: str | None = None, metadata: FileInfo | None = None
24+
) -> dict | list[dict]:
2125
if metadata is None:
2226
metadata = FileInfo()
2327
if base_dir and not metadata.base_path:
@@ -30,24 +34,23 @@ def load_as_dict(
3034
if metadata and metadata.source_file:
3135
data.name = os.path.relpath(metadata.source_file, metadata.base_path)
3236
return yaml.load(data, DupCheckYamlLoader)
33-
else:
34-
return data
37+
return data
3538

3639
def load_any(
3740
self,
38-
source: Union[str, dict, TextIO],
39-
target_class: Union[type[YAMLRoot], type[BaseModel]],
41+
source: str | dict | TextIO,
42+
target_class: type[YAMLRoot | BaseModel],
4043
*,
41-
base_dir: Optional[str] = None,
42-
metadata: Optional[FileInfo] = None,
44+
base_dir: str | None = None,
45+
metadata: FileInfo | None = None,
4346
**_,
44-
) -> Union[YAMLRoot, list[YAMLRoot]]:
47+
) -> YAMLRoot | list[YAMLRoot]:
4548
data_as_dict = self.load_as_dict(source, base_dir=base_dir, metadata=metadata)
4649
return self._construct_target_class(data_as_dict, target_class)
4750

4851
def loads_any(
49-
self, source: str, target_class: type[Union[BaseModel, YAMLRoot]], *, metadata: Optional[FileInfo] = None, **_
50-
) -> Union[BaseModel, YAMLRoot, list[BaseModel], list[YAMLRoot]]:
52+
self, source: str, target_class: type[BaseModel | YAMLRoot], *, metadata: FileInfo | None = None, **_
53+
) -> BaseModel | YAMLRoot | list[BaseModel] | list[YAMLRoot]:
5154
"""
5255
Load source as a string
5356
@param source: source

0 commit comments

Comments
 (0)