Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/rdflib/namespace.py @ 5:9b1c78e6ba9c draft default tip
"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author | shellac |
---|---|
date | Mon, 01 Jun 2020 08:59:25 -0400 |
parents | 79f47841a781 |
children |
comparison
equal
deleted
inserted
replaced
4:79f47841a781 | 5:9b1c78e6ba9c |
---|---|
1 from rdflib.py3compat import format_doctest_out | |
2 | |
3 __doc__ = format_doctest_out(""" | |
4 =================== | |
5 Namespace Utilities | |
6 =================== | |
7 | |
8 RDFLib provides mechanisms for managing Namespaces. | |
9 | |
10 In particular, there is a :class:`~rdflib.namespace.Namespace` class | |
11 that takes as its argument the base URI of the namespace. | |
12 | |
13 .. code-block:: pycon | |
14 | |
15 >>> from rdflib.namespace import Namespace | |
16 >>> owl = Namespace('http://www.w3.org/2002/07/owl#') | |
17 | |
18 Fully qualified URIs in the namespace can be constructed either by attribute | |
19 or by dictionary access on Namespace instances: | |
20 | |
21 .. code-block:: pycon | |
22 | |
23 >>> owl.seeAlso | |
24 rdflib.term.URIRef(%(u)s'http://www.w3.org/2002/07/owl#seeAlso') | |
25 >>> owl['seeAlso'] | |
26 rdflib.term.URIRef(%(u)s'http://www.w3.org/2002/07/owl#seeAlso') | |
27 | |
28 | |
29 Automatic handling of unknown predicates | |
30 ----------------------------------------- | |
31 | |
32 As a programming convenience, a namespace binding is automatically | |
33 created when :class:`rdflib.term.URIRef` predicates are added to the graph. | |
34 | |
35 Importable namespaces | |
36 ----------------------- | |
37 | |
38 The following namespaces are available by directly importing from rdflib: | |
39 | |
40 * RDF | |
41 * RDFS | |
42 * OWL | |
43 * XSD | |
44 * FOAF | |
45 * SKOS | |
46 * DOAP | |
47 * DC | |
48 * DCTERMS | |
49 * VOID | |
50 | |
51 .. code-block:: pycon | |
52 | |
53 >>> from rdflib import OWL | |
54 >>> OWL.seeAlso | |
55 rdflib.term.URIRef(%(u)s'http://www.w3.org/2002/07/owl#seeAlso') | |
56 | |
57 """) | |
58 | |
59 import logging | |
60 logger = logging.getLogger(__name__) | |
61 | |
62 import os | |
63 | |
64 from urllib.parse import urljoin, urldefrag | |
65 from urllib.request import pathname2url | |
66 | |
67 from rdflib.term import URIRef, Variable, _XSD_PFX, _is_valid_uri | |
68 | |
69 __all__ = [ | |
70 'is_ncname', 'split_uri', 'Namespace', | |
71 'ClosedNamespace', 'NamespaceManager', | |
72 'XMLNS', 'RDF', 'RDFS', 'XSD', 'OWL', | |
73 'SKOS', 'DOAP', 'FOAF', 'DC', 'DCTERMS', 'VOID'] | |
74 | |
75 | |
76 class Namespace(str): | |
77 | |
78 __doc__ = format_doctest_out(""" | |
79 Utility class for quickly generating URIRefs with a common prefix | |
80 | |
81 >>> from rdflib import Namespace | |
82 >>> n = Namespace("http://example.org/") | |
83 >>> n.Person # as attribute | |
84 rdflib.term.URIRef(%(u)s'http://example.org/Person') | |
85 >>> n['first-name'] # as item - for things that are not valid python identifiers | |
86 rdflib.term.URIRef(%(u)s'http://example.org/first-name') | |
87 | |
88 """) | |
89 | |
90 | |
91 def __new__(cls, value): | |
92 try: | |
93 rt = str.__new__(cls, value) | |
94 except UnicodeDecodeError: | |
95 rt = str.__new__(cls, value, 'utf-8') | |
96 return rt | |
97 | |
98 | |
99 @property | |
100 def title(self): | |
101 return URIRef(self + 'title') | |
102 | |
103 def term(self, name): | |
104 # need to handle slices explicitly because of __getitem__ override | |
105 return URIRef(self + (name if isinstance(name, str) else '')) | |
106 | |
107 def __getitem__(self, key, default=None): | |
108 return self.term(key) | |
109 | |
110 def __getattr__(self, name): | |
111 if name.startswith("__"): # ignore any special Python names! | |
112 raise AttributeError | |
113 else: | |
114 return self.term(name) | |
115 | |
116 def __repr__(self): | |
117 return "Namespace(%s)"%str.__repr__(self) | |
118 | |
119 | |
120 class URIPattern(str): | |
121 | |
122 __doc__ = format_doctest_out(""" | |
123 Utility class for creating URIs according to some pattern | |
124 This supports either new style formatting with .format | |
125 or old-style with %% operator | |
126 | |
127 >>> u=URIPattern("http://example.org/%%s/%%d/resource") | |
128 >>> u%%('books', 12345) | |
129 rdflib.term.URIRef(%(u)s'http://example.org/books/12345/resource') | |
130 | |
131 """) | |
132 | |
133 def __new__(cls, value): | |
134 try: | |
135 rt = str.__new__(cls, value) | |
136 except UnicodeDecodeError: | |
137 rt = str.__new__(cls, value, 'utf-8') | |
138 return rt | |
139 | |
140 def __mod__(self, *args, **kwargs): | |
141 return URIRef(str(self).__mod__(*args, **kwargs)) | |
142 | |
143 def format(self, *args, **kwargs): | |
144 return URIRef(str.format(self, *args, **kwargs)) | |
145 | |
146 def __repr__(self): | |
147 return "URIPattern(%r)"%str.__repr__(self) | |
148 | |
149 | |
150 | |
151 class ClosedNamespace(object): | |
152 """ | |
153 A namespace with a closed list of members | |
154 | |
155 Trying to create terms not listen is an error | |
156 """ | |
157 | |
158 def __init__(self, uri, terms): | |
159 self.uri = uri | |
160 self.__uris = {} | |
161 for t in terms: | |
162 self.__uris[t] = URIRef(self.uri + t) | |
163 | |
164 def term(self, name): | |
165 uri = self.__uris.get(name) | |
166 if uri is None: | |
167 raise Exception( | |
168 "term '%s' not in namespace '%s'" % (name, self.uri)) | |
169 else: | |
170 return uri | |
171 | |
172 def __getitem__(self, key, default=None): | |
173 return self.term(key) | |
174 | |
175 def __getattr__(self, name): | |
176 if name.startswith("__"): # ignore any special Python names! | |
177 raise AttributeError | |
178 else: | |
179 return self.term(name) | |
180 | |
181 def __str__(self): | |
182 return str(self.uri) | |
183 | |
184 def __repr__(self): | |
185 return """rdf.namespace.ClosedNamespace('%s')""" % str(self.uri) | |
186 | |
187 | |
188 class _RDFNamespace(ClosedNamespace): | |
189 """ | |
190 Closed namespace for RDF terms | |
191 """ | |
192 def __init__(self): | |
193 super(_RDFNamespace, self).__init__( | |
194 URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#"), | |
195 terms=[ | |
196 # Syntax Names | |
197 "RDF", "Description", "ID", "about", "parseType", | |
198 "resource", "li", "nodeID", "datatype", | |
199 | |
200 # RDF Classes | |
201 "Seq", "Bag", "Alt", "Statement", "Property", | |
202 "List", "PlainLiteral", | |
203 | |
204 # RDF Properties | |
205 "subject", "predicate", "object", "type", | |
206 "value", "first", "rest", | |
207 # and _n where n is a non-negative integer | |
208 | |
209 # RDF Resources | |
210 "nil", | |
211 | |
212 # Added in RDF 1.1 | |
213 "XMLLiteral", "HTML", "langString"] | |
214 ) | |
215 | |
216 def term(self, name): | |
217 try: | |
218 i = int(name) | |
219 return URIRef("%s_%s" % (self.uri, i)) | |
220 except ValueError: | |
221 return super(_RDFNamespace, self).term(name) | |
222 | |
223 RDF = _RDFNamespace() | |
224 | |
225 RDFS = ClosedNamespace( | |
226 uri=URIRef("http://www.w3.org/2000/01/rdf-schema#"), | |
227 terms=[ | |
228 "Resource", "Class", "subClassOf", "subPropertyOf", "comment", "label", | |
229 "domain", "range", "seeAlso", "isDefinedBy", "Literal", "Container", | |
230 "ContainerMembershipProperty", "member", "Datatype"] | |
231 ) | |
232 | |
233 OWL = Namespace('http://www.w3.org/2002/07/owl#') | |
234 | |
235 XSD = Namespace(_XSD_PFX) | |
236 | |
237 SKOS = Namespace('http://www.w3.org/2004/02/skos/core#') | |
238 DOAP = Namespace('http://usefulinc.com/ns/doap#') | |
239 FOAF = Namespace('http://xmlns.com/foaf/0.1/') | |
240 DC = Namespace('http://purl.org/dc/elements/1.1/') | |
241 DCTERMS = Namespace('http://purl.org/dc/terms/') | |
242 VOID = Namespace('http://rdfs.org/ns/void#') | |
243 | |
244 | |
245 | |
246 class NamespaceManager(object): | |
247 """ | |
248 | |
249 Class for managing prefix => namespace mappings | |
250 | |
251 Sample usage from FuXi ... | |
252 | |
253 .. code-block:: python | |
254 | |
255 ruleStore = N3RuleStore(additionalBuiltins=additionalBuiltins) | |
256 nsMgr = NamespaceManager(Graph(ruleStore)) | |
257 ruleGraph = Graph(ruleStore,namespace_manager=nsMgr) | |
258 | |
259 | |
260 and ... | |
261 | |
262 .. code-block:: pycon | |
263 | |
264 >>> import rdflib | |
265 >>> from rdflib import Graph | |
266 >>> from rdflib.namespace import Namespace, NamespaceManager | |
267 >>> exNs = Namespace('http://example.com/') | |
268 >>> namespace_manager = NamespaceManager(Graph()) | |
269 >>> namespace_manager.bind('ex', exNs, override=False) | |
270 >>> g = Graph() | |
271 >>> g.namespace_manager = namespace_manager | |
272 >>> all_ns = [n for n in g.namespace_manager.namespaces()] | |
273 >>> assert ('ex', rdflib.term.URIRef('http://example.com/')) in all_ns | |
274 >>> | |
275 | |
276 """ | |
277 def __init__(self, graph): | |
278 self.graph = graph | |
279 self.__cache = {} | |
280 self.__log = None | |
281 self.bind("xml", "http://www.w3.org/XML/1998/namespace") | |
282 self.bind("rdf", RDF) | |
283 self.bind("rdfs", RDFS) | |
284 self.bind("xsd", XSD) | |
285 | |
286 def reset(self): | |
287 self.__cache = {} | |
288 | |
289 def __get_store(self): | |
290 return self.graph.store | |
291 store = property(__get_store) | |
292 | |
293 def qname(self, uri): | |
294 prefix, namespace, name = self.compute_qname(uri) | |
295 if prefix == "": | |
296 return name | |
297 else: | |
298 return ":".join((prefix, name)) | |
299 | |
300 def normalizeUri(self, rdfTerm): | |
301 """ | |
302 Takes an RDF Term and 'normalizes' it into a QName (using the | |
303 registered prefix) or (unlike compute_qname) the Notation 3 | |
304 form for URIs: <...URI...> | |
305 """ | |
306 try: | |
307 namespace, name = split_uri(rdfTerm) | |
308 namespace = URIRef(str(namespace)) | |
309 except: | |
310 if isinstance(rdfTerm, Variable): | |
311 return "?%s" % rdfTerm | |
312 else: | |
313 return "<%s>" % rdfTerm | |
314 prefix = self.store.prefix(namespace) | |
315 if prefix is None and isinstance(rdfTerm, Variable): | |
316 return "?%s" % rdfTerm | |
317 elif prefix is None: | |
318 return "<%s>" % rdfTerm | |
319 else: | |
320 qNameParts = self.compute_qname(rdfTerm) | |
321 return ':'.join([qNameParts[0], qNameParts[-1]]) | |
322 | |
323 def compute_qname(self, uri, generate=True): | |
324 | |
325 if not _is_valid_uri(uri): | |
326 raise Exception('"%s" does not look like a valid URI, I cannot serialize this. Perhaps you wanted to urlencode it?'%uri) | |
327 | |
328 | |
329 if not uri in self.__cache: | |
330 namespace, name = split_uri(uri) | |
331 namespace = URIRef(namespace) | |
332 prefix = self.store.prefix(namespace) | |
333 if prefix is None: | |
334 if not generate: | |
335 raise Exception( | |
336 "No known prefix for %s and generate=False") | |
337 num = 1 | |
338 while 1: | |
339 prefix = "ns%s" % num | |
340 if not self.store.namespace(prefix): | |
341 break | |
342 num += 1 | |
343 self.bind(prefix, namespace) | |
344 self.__cache[uri] = (prefix, namespace, name) | |
345 return self.__cache[uri] | |
346 | |
347 def bind(self, prefix, namespace, override=True, replace=False): | |
348 | |
349 """bind a given namespace to the prefix | |
350 | |
351 if override, rebind, even if the given namespace is already | |
352 bound to another prefix. | |
353 | |
354 if replace, replace any existing prefix with the new namespace | |
355 | |
356 """ | |
357 | |
358 namespace = URIRef(str(namespace)) | |
359 # When documenting explain that override only applies in what cases | |
360 if prefix is None: | |
361 prefix = '' | |
362 bound_namespace = self.store.namespace(prefix) | |
363 # Check if the bound_namespace contains a URI | |
364 # and if so convert it into a URIRef for comparison | |
365 # This is to prevent duplicate namespaces with the | |
366 # same URI | |
367 if bound_namespace: | |
368 bound_namespace = URIRef(bound_namespace) | |
369 if bound_namespace and bound_namespace != namespace: | |
370 | |
371 if replace: | |
372 self.store.bind(prefix, namespace) | |
373 return | |
374 | |
375 # prefix already in use for different namespace | |
376 # | |
377 # append number to end of prefix until we find one | |
378 # that's not in use. | |
379 if not prefix: | |
380 prefix = "default" | |
381 num = 1 | |
382 while 1: | |
383 new_prefix = "%s%s" % (prefix, num) | |
384 tnamespace = self.store.namespace(new_prefix) | |
385 if tnamespace and namespace == URIRef(tnamespace): | |
386 # the prefix is already bound to the correct | |
387 # namespace | |
388 return | |
389 if not self.store.namespace(new_prefix): | |
390 break | |
391 num += 1 | |
392 self.store.bind(new_prefix, namespace) | |
393 else: | |
394 bound_prefix = self.store.prefix(namespace) | |
395 if bound_prefix is None: | |
396 self.store.bind(prefix, namespace) | |
397 elif bound_prefix == prefix: | |
398 pass # already bound | |
399 else: | |
400 if override or bound_prefix.startswith("_"): # or a generated | |
401 # prefix | |
402 self.store.bind(prefix, namespace) | |
403 | |
404 def namespaces(self): | |
405 for prefix, namespace in self.store.namespaces(): | |
406 namespace = URIRef(namespace) | |
407 yield prefix, namespace | |
408 | |
409 def absolutize(self, uri, defrag=1): | |
410 base = urljoin("file:", pathname2url(os.getcwd())) | |
411 result = urljoin("%s/" % base, uri, allow_fragments=not defrag) | |
412 if defrag: | |
413 result = urldefrag(result)[0] | |
414 if not defrag: | |
415 if uri and uri[-1] == "#" and result[-1] != "#": | |
416 result = "%s#" % result | |
417 return URIRef(result) | |
418 | |
419 # From: http://www.w3.org/TR/REC-xml#NT-CombiningChar | |
420 # | |
421 # * Name start characters must have one of the categories Ll, Lu, Lo, | |
422 # Lt, Nl. | |
423 # | |
424 # * Name characters other than Name-start characters must have one of | |
425 # the categories Mc, Me, Mn, Lm, or Nd. | |
426 # | |
427 # * Characters in the compatibility area (i.e. with character code | |
428 # greater than #xF900 and less than #xFFFE) are not allowed in XML | |
429 # names. | |
430 # | |
431 # * Characters which have a font or compatibility decomposition | |
432 # (i.e. those with a "compatibility formatting tag" in field 5 of the | |
433 # database -- marked by field 5 beginning with a "<") are not allowed. | |
434 # | |
435 # * The following characters are treated as name-start characters rather | |
436 # than name characters, because the property file classifies them as | |
437 # Alphabetic: [#x02BB-#x02C1], #x0559, #x06E5, #x06E6. | |
438 # | |
439 # * Characters #x20DD-#x20E0 are excluded (in accordance with Unicode | |
440 # 2.0, section 5.14). | |
441 # | |
442 # * Character #x00B7 is classified as an extender, because the property | |
443 # list so identifies it. | |
444 # | |
445 # * Character #x0387 is added as a name character, because #x00B7 is its | |
446 # canonical equivalent. | |
447 # | |
448 # * Characters ':' and '_' are allowed as name-start characters. | |
449 # | |
450 # * Characters '-' and '.' are allowed as name characters. | |
451 | |
452 from unicodedata import category | |
453 | |
454 NAME_START_CATEGORIES = ["Ll", "Lu", "Lo", "Lt", "Nl"] | |
455 NAME_CATEGORIES = NAME_START_CATEGORIES + ["Mc", "Me", "Mn", "Lm", "Nd"] | |
456 ALLOWED_NAME_CHARS = ["\u00B7", "\u0387", "-", ".", "_"] | |
457 | |
458 # http://www.w3.org/TR/REC-xml-names/#NT-NCName | |
459 # [4] NCName ::= (Letter | '_') (NCNameChar)* /* An XML Name, minus | |
460 # the ":" */ | |
461 # [5] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | |
462 # | Extender | |
463 | |
464 | |
465 def is_ncname(name): | |
466 first = name[0] | |
467 if first == "_" or category(first) in NAME_START_CATEGORIES: | |
468 for i in range(1, len(name)): | |
469 c = name[i] | |
470 if not category(c) in NAME_CATEGORIES: | |
471 if c in ALLOWED_NAME_CHARS: | |
472 continue | |
473 return 0 | |
474 # if in compatibility area | |
475 # if decomposition(c)!='': | |
476 # return 0 | |
477 | |
478 return 1 | |
479 else: | |
480 return 0 | |
481 | |
482 XMLNS = "http://www.w3.org/XML/1998/namespace" | |
483 | |
484 | |
485 def split_uri(uri): | |
486 if uri.startswith(XMLNS): | |
487 return (XMLNS, uri.split(XMLNS)[1]) | |
488 length = len(uri) | |
489 for i in range(0, length): | |
490 c = uri[-i - 1] | |
491 if not category(c) in NAME_CATEGORIES: | |
492 if c in ALLOWED_NAME_CHARS: | |
493 continue | |
494 for j in range(-1 - i, length): | |
495 if category(uri[j]) in NAME_START_CATEGORIES or uri[j] == "_": | |
496 ns = uri[:j] | |
497 if not ns: | |
498 break | |
499 ln = uri[j:] | |
500 return (ns, ln) | |
501 break | |
502 raise Exception("Can't split '%s'" % uri) |