comparison env/lib/python3.7/site-packages/schema_salad/jsonld_context.py @ 0:26e78fe6e8c4 draft

"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author shellac
date Sat, 02 May 2020 07:14:21 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:26e78fe6e8c4
1 from __future__ import absolute_import
2
3 import logging
4 from typing import (
5 Any,
6 Dict,
7 Iterable,
8 List,
9 MutableMapping,
10 MutableSequence,
11 Optional,
12 Tuple,
13 Union,
14 cast,
15 )
16
17 import rdflib
18 import rdflib.namespace
19 import six
20 from rdflib import Graph, URIRef
21 from rdflib.namespace import RDF, RDFS
22 from six.moves import urllib
23 from typing_extensions import Text # pylint: disable=unused-import
24
25 from .exceptions import SchemaException
26 from .ref_resolver import ContextType # pylint: disable=unused-import
27 from .utils import aslist, json_dumps
28
29 # move to a regular typing import when Python 3.3-3.6 is no longer supported
30
31
32 _logger = logging.getLogger("salad")
33
34
35 def pred(
36 datatype, # type: MutableMapping[Text, Union[Dict[Text, Text], Text]]
37 field, # type: Optional[Dict[Text, Any]]
38 name, # type: str
39 context, # type: ContextType
40 defaultBase, # type: str
41 namespaces, # type: Dict[Text, rdflib.namespace.Namespace]
42 ): # type: (...) -> Union[Dict[Text, Text], Text]
43 split = urllib.parse.urlsplit(name)
44
45 vee = None # type: Optional[Text]
46
47 if split.scheme != "":
48 vee = name
49 (ns, ln) = rdflib.namespace.split_uri(six.text_type(vee))
50 name = ln
51 if ns[0:-1] in namespaces:
52 vee = six.text_type(namespaces[ns[0:-1]][ln])
53 _logger.debug("name, v %s %s", name, vee)
54
55 v = None # type: Optional[Dict[Text, Any]]
56
57 if field is not None and "jsonldPredicate" in field:
58 if isinstance(field["jsonldPredicate"], MutableMapping):
59 v = {}
60 for k, val in field["jsonldPredicate"].items():
61 v[("@" + k[1:] if k.startswith("_") else k)] = val
62 if "@id" not in v:
63 v["@id"] = vee
64 else:
65 v = field["jsonldPredicate"]
66 elif "jsonldPredicate" in datatype:
67 if isinstance(datatype["jsonldPredicate"], Iterable):
68 for d in datatype["jsonldPredicate"]:
69 if isinstance(d, MutableMapping):
70 if d["symbol"] == name:
71 v = d["predicate"]
72 else:
73 raise SchemaException(
74 "entries in the jsonldPredicate List must be " "Dictionaries"
75 )
76 else:
77 raise SchemaException("jsonldPredicate must be a List of Dictionaries.")
78
79 ret = v or vee
80
81 if not ret:
82 ret = defaultBase + name
83
84 if name in context:
85 if context[name] != ret:
86 raise SchemaException(
87 "Predicate collision on {}, '{}' != '{}'".format(
88 name, context[name], ret
89 )
90 )
91 else:
92 _logger.debug("Adding to context '%s' %s (%s)", name, ret, type(ret))
93 context[name] = ret
94
95 return ret
96
97
98 def process_type(
99 t, # type: MutableMapping[Text, Any]
100 g, # type: Graph
101 context, # type: ContextType
102 defaultBase, # type: str
103 namespaces, # type: Dict[Text, rdflib.namespace.Namespace]
104 defaultPrefix, # type: str
105 ): # type: (...) -> None
106 if t["type"] not in ("record", "enum"):
107 return
108
109 if "name" in t:
110 recordname = t["name"]
111
112 _logger.debug("Processing %s %s\n", t.get("type"), t)
113
114 classnode = URIRef(recordname)
115 g.add((classnode, RDF.type, RDFS.Class))
116
117 split = urllib.parse.urlsplit(recordname)
118 predicate = recordname
119 if t.get("inVocab", True):
120 if split.scheme:
121 (ns, ln) = rdflib.namespace.split_uri(six.text_type(recordname))
122 predicate = recordname
123 recordname = ln
124 else:
125 predicate = "{}:{}".format(defaultPrefix, recordname)
126
127 if context.get(recordname, predicate) != predicate:
128 raise SchemaException(
129 "Predicate collision on '{}', '{}' != '{}'".format(
130 recordname, context[recordname], predicate
131 )
132 )
133
134 if not recordname:
135 raise SchemaException("Unable to find/derive recordname for {}".format(t))
136
137 _logger.debug(
138 "Adding to context '%s' %s (%s)", recordname, predicate, type(predicate)
139 )
140 context[recordname] = predicate
141
142 if t["type"] == "record":
143 for i in t.get("fields", []):
144 fieldname = i["name"]
145
146 _logger.debug("Processing field %s", i)
147
148 v = pred(
149 t, i, fieldname, context, defaultPrefix, namespaces
150 ) # type: Union[Dict[Any, Any], Text, None]
151
152 if isinstance(v, six.string_types):
153 v = v if v[0] != "@" else None
154 elif v is not None:
155 v = v["_@id"] if v.get("_@id", "@")[0] != "@" else None
156
157 if bool(v):
158 (ns, ln) = rdflib.namespace.split_uri(six.text_type(v))
159 if ns[0:-1] in namespaces:
160 propnode = namespaces[ns[0:-1]][ln]
161 else:
162 propnode = URIRef(v)
163
164 g.add((propnode, RDF.type, RDF.Property))
165 g.add((propnode, RDFS.domain, classnode))
166
167 # TODO generate range from datatype.
168
169 if isinstance(i["type"], MutableMapping):
170 process_type(
171 i["type"], g, context, defaultBase, namespaces, defaultPrefix
172 )
173
174 if "extends" in t:
175 for e in aslist(t["extends"]):
176 g.add((classnode, RDFS.subClassOf, URIRef(e)))
177 elif t["type"] == "enum":
178 _logger.debug("Processing enum %s", t.get("name"))
179
180 for i in t["symbols"]:
181 pred(t, None, i, context, defaultBase, namespaces)
182
183
184 def salad_to_jsonld_context(
185 j, # type: Iterable[MutableMapping[Text, Any]]
186 schema_ctx, # type: MutableMapping[Text, Any]
187 ): # type: (...) -> Tuple[ContextType, Graph]
188 context = {} # type: ContextType
189 namespaces = {}
190 g = Graph()
191 defaultPrefix = ""
192
193 for k, v in schema_ctx.items():
194 context[k] = v
195 namespaces[k] = rdflib.namespace.Namespace(v)
196
197 if "@base" in context:
198 defaultBase = cast(str, context["@base"])
199 del context["@base"]
200 else:
201 defaultBase = ""
202
203 for k, v in namespaces.items():
204 g.bind(str(k), v)
205
206 for t in j:
207 process_type(t, g, context, defaultBase, namespaces, defaultPrefix)
208
209 return (context, g)
210
211
212 def fix_jsonld_ids(
213 obj, # type: Union[List[Dict[Text, Any]], MutableMapping[Text, Any]]
214 ids, # type: List[Text]
215 ): # type: (...) -> None
216 if isinstance(obj, MutableMapping):
217 for i in ids:
218 if i in obj:
219 obj["@id"] = obj[i]
220 for v in obj.values():
221 fix_jsonld_ids(v, ids)
222 if isinstance(obj, MutableSequence):
223 for entry in obj:
224 fix_jsonld_ids(entry, ids)
225
226
227 def makerdf(
228 workflow, # type: Text
229 wf, # type: Union[List[Dict[Text, Any]], MutableMapping[Text, Any]]
230 ctx, # type: ContextType
231 graph=None, # type: Optional[Graph]
232 ): # type: (...) -> Graph
233 prefixes = {}
234 idfields = []
235 for k, v in six.iteritems(ctx):
236 if isinstance(v, MutableMapping):
237 url = v["@id"]
238 else:
239 url = v
240 if url == "@id":
241 idfields.append(k)
242 doc_url, frg = urllib.parse.urldefrag(url)
243 if "/" in frg:
244 p = frg.split("/")[0]
245 prefixes[p] = u"{}#{}/".format(doc_url, p)
246
247 fix_jsonld_ids(wf, idfields)
248
249 if graph is None:
250 g = Graph()
251 else:
252 g = graph
253
254 if isinstance(wf, MutableSequence):
255 for w in wf:
256 w["@context"] = ctx
257 g.parse(data=json_dumps(w), format="json-ld", publicID=str(workflow))
258 else:
259 wf["@context"] = ctx
260 g.parse(data=json_dumps(wf), format="json-ld", publicID=str(workflow))
261
262 # Bug in json-ld loader causes @id fields to be added to the graph
263 for sub, pred, obj in g.triples((None, URIRef("@id"), None)):
264 g.remove((sub, pred, obj))
265
266 for k2, v2 in six.iteritems(prefixes):
267 g.namespace_manager.bind(k2, v2)
268
269 return g