|
1 | 1 | from abc import ABC, abstractmethod |
2 | 2 | from typing import TextIO, Union, Optional, Callable, Dict, Type, Any, List |
| 3 | +from logging import getLogger |
| 4 | +from functools import lru_cache |
3 | 5 |
|
4 | 6 | from pydantic import BaseModel |
5 | 7 | from hbreader import FileInfo, hbread |
6 | 8 | from jsonasobj2 import as_dict, JsonObj |
7 | 9 |
|
8 | 10 | from linkml_runtime.utils.yamlutils import YAMLRoot |
| 11 | +from linkml_runtime import URI_TO_LOCAL |
| 12 | + |
| 13 | +CACHE_SIZE = 1024 |
| 14 | + |
9 | 15 |
|
10 | 16 |
|
11 | 17 | class Loader(ABC): |
@@ -137,20 +143,42 @@ def _construct_target_class(self, |
137 | 143 | else: |
138 | 144 | return None |
139 | 145 |
|
| 146 | + |
140 | 147 | def _read_source(self, |
141 | 148 | source: Union[str, dict, TextIO], |
142 | 149 | *, |
143 | 150 | base_dir: Optional[str] = None, |
144 | 151 | metadata: Optional[FileInfo] = None, |
145 | 152 | accept_header: Optional[str] = "text/plain, application/yaml;q=0.9") -> Union[dict, str]: |
146 | | - if metadata is None: |
147 | | - metadata = FileInfo() |
148 | | - if base_dir and not metadata.base_path: |
149 | | - metadata.base_path = base_dir |
| 153 | + |
| 154 | + # avoid instantiating unhashable FileInfo type by getting default base_path, if any |
| 155 | + if base_dir is None: |
| 156 | + if metadata is not None: |
| 157 | + base_dir = str(metadata.base_path) if metadata.base_path is not None else None |
| 158 | + else: |
| 159 | + base_dir = str(FileInfo().base_path) if FileInfo().base_path is not None else None |
| 160 | + elif base_dir is not None: |
| 161 | + base_dir = str(base_dir) |
150 | 162 |
|
151 | 163 | if not isinstance(source, dict): |
152 | | - data = hbread(source, metadata, metadata.base_path, accept_header) |
| 164 | + # Try to get local version of schema, if one is known to exist |
| 165 | + try: |
| 166 | + if str(source) in URI_TO_LOCAL.keys(): |
| 167 | + source = str(URI_TO_LOCAL[str(source)]) |
| 168 | + except (TypeError, KeyError) as e: |
| 169 | + # Fine, use original `source` value |
| 170 | + logger = getLogger('linkml_runtime.loaders.Loader') |
| 171 | + logger.debug(f"Error converting stringlike source to local linkml file: {source}, got: {e}") |
| 172 | + |
| 173 | + if not isinstance(metadata, FileInfo): |
| 174 | + data = self._hashable_read(source, base_dir, accept_header) |
| 175 | + else: |
| 176 | + data = hbread(source, metadata, base_dir, accept_header) |
153 | 177 | else: |
154 | 178 | data = source |
155 | 179 |
|
156 | 180 | return data |
| 181 | + |
| 182 | + @lru_cache(maxsize=CACHE_SIZE) |
| 183 | + def _hashable_read(self, source: Union[dict, TextIO], base_dir: Optional[str] = None, accept_header: Optional[str] = None): |
| 184 | + return hbread(source, None, base_dir, accept_header) |
0 commit comments