__init__.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032
  1. """
  2. # Namespace Utilities
  3. RDFLib provides mechanisms for managing Namespaces.
  4. In particular, there is a [`Namespace`][rdflib.namespace.Namespace] class
  5. that takes as its argument the base URI of the namespace.
  6. ```python
  7. >>> from rdflib.namespace import Namespace
  8. >>> RDFS = Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#")
  9. ```
  10. Fully qualified URIs in the namespace can be constructed either by attribute
  11. or by dictionary access on Namespace instances:
  12. ```python
  13. >>> RDFS.seeAlso
  14. rdflib.term.URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#seeAlso')
  15. >>> RDFS['seeAlso']
  16. rdflib.term.URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#seeAlso')
  17. ```
  18. ## Automatic handling of unknown predicates
  19. As a programming convenience, a namespace binding is automatically
  20. created when [`URIRef`][rdflib.term.URIRef] predicates are added to the graph.
  21. ## Importable namespaces
  22. The following namespaces are available by directly importing from rdflib:
  23. * BRICK
  24. * CSVW
  25. * DC
  26. * DCAT
  27. * DCMITYPE
  28. * DCTERMS
  29. * DCAM
  30. * DOAP
  31. * FOAF
  32. * ODRL2
  33. * ORG
  34. * OWL
  35. * PROF
  36. * PROV
  37. * QB
  38. * RDF
  39. * RDFS
  40. * SDO
  41. * SH
  42. * SKOS
  43. * SOSA
  44. * SSN
  45. * TIME
  46. * VANN
  47. * VOID
  48. * WGS
  49. * XSD
  50. ```python
  51. >>> from rdflib.namespace import RDFS
  52. >>> RDFS.seeAlso
  53. rdflib.term.URIRef('http://www.w3.org/2000/01/rdf-schema#seeAlso')
  54. ```
  55. """
  56. from __future__ import annotations
  57. import logging
  58. import warnings
  59. try:
  60. # Python >= 3.14
  61. from annotationlib import (
  62. get_annotations, # type: ignore[attr-defined,unused-ignore]
  63. )
  64. except ImportError: # pragma: no cover
  65. try:
  66. # Python >= 3.10
  67. from inspect import get_annotations # type: ignore[attr-defined,unused-ignore]
  68. except ImportError:
  69. def get_annotations(thing: Any) -> dict:
  70. return thing.__annotations__
  71. from functools import lru_cache
  72. from pathlib import Path
  73. from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Set, Tuple, Union
  74. from unicodedata import category
  75. from urllib.parse import urldefrag, urljoin
  76. from rdflib.term import URIRef, Variable, _is_valid_uri
  77. if TYPE_CHECKING:
  78. from rdflib.graph import Graph
  79. from rdflib.store import Store
  80. __all__ = [
  81. "is_ncname",
  82. "split_uri",
  83. "Namespace",
  84. "ClosedNamespace",
  85. "DefinedNamespace",
  86. "NamespaceManager",
  87. "BRICK",
  88. "CSVW",
  89. "DC",
  90. "DCAM",
  91. "DCAT",
  92. "DCMITYPE",
  93. "DCTERMS",
  94. "DOAP",
  95. "FOAF",
  96. "GEO",
  97. "ODRL2",
  98. "ORG",
  99. "OWL",
  100. "PROF",
  101. "PROV",
  102. "QB",
  103. "RDF",
  104. "RDFS",
  105. "SDO",
  106. "SH",
  107. "SKOS",
  108. "SOSA",
  109. "SSN",
  110. "TIME",
  111. "VANN",
  112. "VOID",
  113. "WGS",
  114. "XSD",
  115. ]
  116. logger = logging.getLogger(__name__)
  117. class Namespace(str):
  118. """Utility class for quickly generating URIRefs with a common prefix.
  119. ```python
  120. >>> from rdflib.namespace import Namespace
  121. >>> n = Namespace("http://example.org/")
  122. >>> n.Person # as attribute
  123. rdflib.term.URIRef('http://example.org/Person')
  124. >>> n['first-name'] # as item - for things that are not valid python identifiers
  125. rdflib.term.URIRef('http://example.org/first-name')
  126. >>> n.Person in n
  127. True
  128. >>> n2 = Namespace("http://example2.org/")
  129. >>> n.Person in n2
  130. False
  131. ```
  132. """
  133. def __new__(cls, value: Union[str, bytes]) -> Namespace:
  134. try:
  135. rt = str.__new__(cls, value)
  136. except UnicodeDecodeError:
  137. rt = str.__new__(cls, value, "utf-8") # type: ignore[arg-type]
  138. return rt
  139. # type error: Signature of "title" incompatible with supertype "str"
  140. @property
  141. def title(self) -> URIRef: # type: ignore[override]
  142. # Override for DCTERMS.title to return a URIRef instead of str.title method
  143. return URIRef(self + "title")
  144. def term(self, name: str) -> URIRef:
  145. # need to handle slices explicitly because of __getitem__ override
  146. return URIRef(self + (name if isinstance(name, str) else ""))
  147. def __getitem__(self, key: str) -> URIRef: # type: ignore[override]
  148. return self.term(key)
  149. def __getattr__(self, name: str) -> URIRef:
  150. if name.startswith("__"): # ignore any special Python names!
  151. raise AttributeError
  152. return self.term(name)
  153. def __repr__(self) -> str:
  154. return f"Namespace({super().__repr__()})"
  155. def __contains__(self, ref: str) -> bool: # type: ignore[override]
  156. """Allows to check if a URI is within (starts with) this Namespace.
  157. ```python
  158. >>> from rdflib import URIRef
  159. >>> namespace = Namespace('http://example.org/')
  160. >>> uri = URIRef('http://example.org/foo')
  161. >>> uri in namespace
  162. True
  163. >>> person_class = namespace['Person']
  164. >>> person_class in namespace
  165. True
  166. >>> obj = URIRef('http://not.example.org/bar')
  167. >>> obj in namespace
  168. False
  169. ```
  170. """
  171. return ref.startswith(self) # test namespace membership with "ref in ns" syntax
  172. class URIPattern(str):
  173. """Utility class for creating URIs according to some pattern.
  174. This supports either new style formatting with .format
  175. or old-style with % operator.
  176. ```python
  177. >>> u=URIPattern("http://example.org/%s/%d/resource")
  178. >>> u%('books', 12345)
  179. rdflib.term.URIRef('http://example.org/books/12345/resource')
  180. ```
  181. """
  182. def __new__(cls, value: Union[str, bytes]) -> URIPattern:
  183. try:
  184. rt = str.__new__(cls, value)
  185. except UnicodeDecodeError:
  186. if TYPE_CHECKING:
  187. assert isinstance(value, bytes)
  188. rt = str.__new__(cls, value, "utf-8")
  189. return rt
  190. def __mod__(self, *args, **kwargs) -> URIRef:
  191. return URIRef(super().__mod__(*args, **kwargs))
  192. def format(self, *args, **kwargs) -> URIRef:
  193. return URIRef(super().format(*args, **kwargs))
  194. def __repr__(self) -> str:
  195. return f"URIPattern({super().__repr__()})"
  196. # _DFNS_RESERVED_ATTRS are attributes for which DefinedNamespaceMeta should
  197. # always raise AttributeError if they are not defined and which should not be
  198. # considered part of __dir__ results. These should be all annotations on
  199. # `DefinedNamespaceMeta`.
  200. _DFNS_RESERVED_ATTRS: Set[str] = {
  201. "__slots__",
  202. "_NS",
  203. "_warn",
  204. "_fail",
  205. "_extras",
  206. "_underscore_num",
  207. }
  208. # Some libraries probe classes for certain attributes or items.
  209. # This is a list of those attributes and items that should be ignored.
  210. _IGNORED_ATTR_LOOKUP: Set[str] = {
  211. "_pytestfixturefunction", # pytest tries to look this up on Defined namespaces
  212. "_partialmethod", # sphinx tries to look this up during autodoc generation
  213. }
  214. class DefinedNamespaceMeta(type):
  215. """Utility metaclass for generating URIRefs with a common prefix."""
  216. __slots__: Tuple[str, ...] = tuple()
  217. _NS: Namespace
  218. _warn: bool = True
  219. _fail: bool = False # True means mimic ClosedNamespace
  220. _extras: List[str] = [] # List of non-pythonesque items
  221. _underscore_num: bool = False # True means pass "_n" constructs
  222. @lru_cache(maxsize=None)
  223. def __getitem__(cls, name: str, default=None) -> URIRef:
  224. name = str(name)
  225. if name in _DFNS_RESERVED_ATTRS:
  226. raise KeyError(
  227. f"DefinedNamespace like object has no access item named {name!r}"
  228. )
  229. elif name in _IGNORED_ATTR_LOOKUP:
  230. raise KeyError()
  231. if (cls._warn or cls._fail) and name not in cls:
  232. if cls._fail:
  233. raise AttributeError(f"term '{name}' not in namespace '{cls._NS}'")
  234. else:
  235. warnings.warn(
  236. f"Code: {name} is not defined in namespace {cls.__name__}",
  237. stacklevel=3,
  238. )
  239. return cls._NS[name]
  240. def __getattr__(cls, name: str):
  241. if name in _IGNORED_ATTR_LOOKUP:
  242. raise AttributeError()
  243. elif name in _DFNS_RESERVED_ATTRS:
  244. raise AttributeError(
  245. f"DefinedNamespace like object has no attribute {name!r}"
  246. )
  247. elif name.startswith("__"):
  248. return super(DefinedNamespaceMeta, cls).__getattribute__(name)
  249. return cls.__getitem__(name)
  250. def __repr__(cls) -> str:
  251. try:
  252. ns_repr = repr(cls._NS)
  253. except AttributeError:
  254. ns_repr = "<DefinedNamespace>"
  255. return f"Namespace({ns_repr})"
  256. def __str__(cls) -> str:
  257. try:
  258. return str(cls._NS)
  259. except AttributeError:
  260. return "<DefinedNamespace>"
  261. def __add__(cls, other: str) -> URIRef:
  262. return cls.__getitem__(other)
  263. def __contains__(cls, item: str) -> bool:
  264. """Determine whether a URI or an individual item belongs to this namespace"""
  265. try:
  266. this_ns = cls._NS
  267. except AttributeError:
  268. return False
  269. item_str = str(item)
  270. if item_str.startswith(str(this_ns)):
  271. item_str = item_str[len(str(this_ns)) :]
  272. return any(
  273. item_str in get_annotations(c)
  274. or item_str in c._extras
  275. or (cls._underscore_num and item_str[0] == "_" and item_str[1:].isdigit())
  276. for c in cls.mro()
  277. if issubclass(c, DefinedNamespace)
  278. )
  279. def __dir__(cls) -> Iterable[str]:
  280. attrs = {str(x) for x in get_annotations(cls)}
  281. # Removing these as they should not be considered part of the namespace.
  282. attrs.difference_update(_DFNS_RESERVED_ATTRS)
  283. values = {cls[str(x)] for x in attrs}
  284. return values
  285. def as_jsonld_context(self, pfx: str) -> dict: # noqa: N804
  286. """Returns this DefinedNamespace as a JSON-LD 'context' object"""
  287. terms = {pfx: str(self._NS)}
  288. for key, term in get_annotations(self).items():
  289. if issubclass(term, URIRef):
  290. terms[key] = f"{pfx}:{key}"
  291. return {"@context": terms}
  292. class DefinedNamespace(metaclass=DefinedNamespaceMeta):
  293. """A Namespace with an enumerated list of members.
  294. Warnings are emitted if unknown members are referenced if _warn is True.
  295. """
  296. __slots__: Tuple[str, ...] = tuple()
  297. def __init__(self):
  298. raise TypeError("namespace may not be instantiated")
  299. class ClosedNamespace(Namespace):
  300. """
  301. A namespace with a closed list of members
  302. Trying to create terms not listed is an error
  303. """
  304. __uris: Dict[str, URIRef]
  305. def __new__(cls, uri: str, terms: List[str]):
  306. rt = super().__new__(cls, uri)
  307. rt.__uris = {t: URIRef(rt + t) for t in terms} # type: ignore[attr-defined]
  308. return rt
  309. @property
  310. def uri(self) -> str: # Back-compat
  311. return str(self)
  312. def term(self, name: str) -> URIRef:
  313. uri = self.__uris.get(name)
  314. if uri is None:
  315. raise KeyError(f"term '{name}' not in namespace '{self}'")
  316. return uri
  317. def __getitem__(self, key: str) -> URIRef: # type: ignore[override]
  318. return self.term(key)
  319. def __getattr__(self, name: str) -> URIRef:
  320. if name.startswith("__"): # ignore any special Python names!
  321. raise AttributeError
  322. else:
  323. try:
  324. return self.term(name)
  325. except KeyError as e:
  326. raise AttributeError(e)
  327. def __repr__(self) -> str:
  328. return f"{self.__module__}.{self.__class__.__name__}({str(self)!r})"
  329. def __dir__(self) -> List[str]:
  330. return list(self.__uris)
  331. def __contains__(self, ref: str) -> bool: # type: ignore[override]
  332. return (
  333. ref in self.__uris.values()
  334. ) # test namespace membership with "ref in ns" syntax
  335. def _ipython_key_completions_(self) -> List[str]:
  336. return dir(self)
  337. XMLNS = Namespace("http://www.w3.org/XML/1998/namespace")
  338. if TYPE_CHECKING:
  339. from rdflib._type_checking import _NamespaceSetString
  340. _with_bind_override_fix = True
  341. class NamespaceManager:
  342. """Class for managing prefix => namespace mappings
  343. This class requires an RDFlib Graph as an input parameter and may optionally have
  344. the parameter bind_namespaces set. This second parameter selects a strategy which
  345. is one of the following:
  346. * core:
  347. * binds several core RDF prefixes only
  348. * owl, rdf, rdfs, xsd, xml from the NAMESPACE_PREFIXES_CORE object
  349. * rdflib:
  350. * binds all the namespaces shipped with RDFLib as DefinedNamespace instances
  351. * all the core namespaces and all the following: brick, csvw, dc, dcat
  352. * dcmitype, dcterms, dcam, doap, foaf, geo, odrl, org, prof, prov, qb, schema
  353. * sh, skos, sosa, ssn, time, vann, void
  354. * see the NAMESPACE_PREFIXES_RDFLIB object for the up-to-date list
  355. * this is default
  356. * none:
  357. * binds no namespaces to prefixes
  358. * note this is NOT default behaviour
  359. * cc:
  360. * using prefix bindings from prefix.cc which is a online prefixes database
  361. * not implemented yet - this is aspirational
  362. !!! warning "Breaking changes"
  363. The namespaces bound for specific values of `bind_namespaces`
  364. constitute part of RDFLib's public interface, so changes to them should
  365. only be additive within the same minor version. Removing values, or
  366. removing namespaces that are bound by default, constitutes a breaking
  367. change.
  368. See the sample usage
  369. ```python
  370. >>> import rdflib
  371. >>> from rdflib import Graph
  372. >>> from rdflib.namespace import Namespace, NamespaceManager
  373. >>> EX = Namespace('http://example.com/')
  374. >>> namespace_manager = NamespaceManager(Graph())
  375. >>> namespace_manager.bind('ex', EX, override=False)
  376. >>> g = Graph()
  377. >>> g.namespace_manager = namespace_manager
  378. >>> all_ns = [n for n in g.namespace_manager.namespaces()]
  379. >>> assert ('ex', rdflib.term.URIRef('http://example.com/')) in all_ns
  380. ```
  381. """
  382. def __init__(self, graph: Graph, bind_namespaces: _NamespaceSetString = "rdflib"):
  383. self.graph = graph
  384. self.__cache: Dict[str, Tuple[str, URIRef, str]] = {}
  385. self.__cache_strict: Dict[str, Tuple[str, URIRef, str]] = {}
  386. self.__log = None
  387. self.__strie: Dict[str, Any] = {}
  388. self.__trie: Dict[str, Any] = {}
  389. # This type declaration is here becuase there is no common base class
  390. # for all namespaces and without it the inferred type of ns is not
  391. # compatible with all prefixes.
  392. ns: Any
  393. # bind Namespaces as per options.
  394. # default is core
  395. if bind_namespaces == "none":
  396. # binds no namespaces to prefixes
  397. # note this is NOT default
  398. pass
  399. elif bind_namespaces == "rdflib":
  400. # bind all the Namespaces shipped with RDFLib
  401. for prefix, ns in _NAMESPACE_PREFIXES_RDFLIB.items():
  402. self.bind(prefix, ns)
  403. # ... don't forget the core ones too
  404. for prefix, ns in _NAMESPACE_PREFIXES_CORE.items():
  405. self.bind(prefix, ns)
  406. elif bind_namespaces == "cc":
  407. # bind any prefix that can be found with lookups to prefix.cc
  408. # first bind core and rdflib ones
  409. # work out remainder - namespaces without prefixes
  410. # only look those ones up
  411. raise NotImplementedError("Haven't got to this option yet")
  412. elif bind_namespaces == "core":
  413. # bind a few core RDF namespaces - default
  414. for prefix, ns in _NAMESPACE_PREFIXES_CORE.items():
  415. self.bind(prefix, ns)
  416. else:
  417. raise ValueError(f"unsupported namespace set {bind_namespaces}")
  418. def __contains__(self, ref: str) -> bool:
  419. # checks if a reference is in any of the managed namespaces with syntax
  420. # "ref in manager". Note that we don't use "ref in ns", as
  421. # NamespaceManager.namespaces() returns Iterator[Tuple[str, URIRef]]
  422. # rather than Iterator[Tuple[str, Namespace]]
  423. return any(ref.startswith(ns) for prefix, ns in self.namespaces())
  424. def reset(self) -> None:
  425. self.__cache = {}
  426. self.__strie = {}
  427. self.__trie = {}
  428. for p, n in self.namespaces(): # repopulate the trie
  429. insert_trie(self.__trie, str(n))
  430. @property
  431. def store(self) -> Store:
  432. return self.graph.store
  433. def qname(self, uri: str) -> str:
  434. prefix, namespace, name = self.compute_qname(uri)
  435. if prefix == "":
  436. return name
  437. else:
  438. return ":".join((prefix, name))
  439. def curie(self, uri: str, generate: bool = True) -> str:
  440. """
  441. From a URI, generate a valid CURIE.
  442. Result is guaranteed to contain a colon separating the prefix from the
  443. name, even if the prefix is an empty string.
  444. !!! warning "Side-effect"
  445. When `generate` is `True` (which is the default) and there is no
  446. matching namespace for the URI in the namespace manager then a new
  447. namespace will be added with prefix `ns{index}`.
  448. Thus, when `generate` is `True`, this function is not a pure
  449. function because of this side-effect.
  450. This default behaviour is chosen so that this function operates
  451. similarly to `NamespaceManager.qname`.
  452. Args:
  453. uri: URI to generate CURIE for.
  454. generate: Whether to add a prefix for the namespace if one doesn't
  455. already exist. Default: `True`.
  456. Returns:
  457. CURIE for the URI
  458. Raises:
  459. KeyError: If generate is `False` and the namespace doesn't already have
  460. a prefix.
  461. """
  462. prefix, namespace, name = self.compute_qname(uri, generate=generate)
  463. return ":".join((prefix, name))
  464. def qname_strict(self, uri: str) -> str:
  465. prefix, namespace, name = self.compute_qname_strict(uri)
  466. if prefix == "":
  467. return name
  468. else:
  469. return ":".join((prefix, name))
  470. def normalizeUri(self, rdfTerm: str) -> str: # noqa: N802, N803
  471. """
  472. Takes an RDF Term and 'normalizes' it into a QName (using the
  473. registered prefix) or (unlike compute_qname) the Notation 3
  474. form for URIs: <...URI...>
  475. """
  476. try:
  477. namespace, name = split_uri(rdfTerm)
  478. if namespace not in self.__strie:
  479. insert_strie(self.__strie, self.__trie, str(namespace))
  480. namespace = URIRef(str(namespace))
  481. except Exception:
  482. if isinstance(rdfTerm, Variable):
  483. return "?%s" % rdfTerm
  484. else:
  485. return "<%s>" % rdfTerm
  486. prefix = self.store.prefix(namespace)
  487. if prefix is None and isinstance(rdfTerm, Variable):
  488. return "?%s" % rdfTerm
  489. elif prefix is None:
  490. return "<%s>" % rdfTerm
  491. else:
  492. qNameParts = self.compute_qname(rdfTerm) # noqa: N806
  493. return ":".join([qNameParts[0], qNameParts[-1]])
  494. def compute_qname(self, uri: str, generate: bool = True) -> Tuple[str, URIRef, str]:
  495. prefix: Optional[str]
  496. if uri not in self.__cache:
  497. if not _is_valid_uri(uri):
  498. raise ValueError(
  499. '"{}" does not look like a valid URI, cannot serialize this. Did you want to urlencode it?'.format(
  500. uri
  501. )
  502. )
  503. try:
  504. namespace, name = split_uri(uri)
  505. except ValueError as e:
  506. namespace = URIRef(uri)
  507. prefix = self.store.prefix(namespace)
  508. name = "" # empty prefix case, safe since not prefix is error
  509. if not prefix:
  510. raise e
  511. if namespace not in self.__strie:
  512. insert_strie(self.__strie, self.__trie, namespace)
  513. if self.__strie[namespace]:
  514. pl_namespace = get_longest_namespace(self.__strie[namespace], uri)
  515. if pl_namespace is not None:
  516. namespace = pl_namespace
  517. name = uri[len(namespace) :]
  518. namespace = URIRef(namespace)
  519. prefix = self.store.prefix(namespace) # warning multiple prefixes problem
  520. if prefix is None:
  521. if not generate:
  522. raise KeyError(
  523. "No known prefix for {} and generate=False".format(namespace)
  524. )
  525. num = 1
  526. while 1:
  527. prefix = "ns%s" % num
  528. if not self.store.namespace(prefix):
  529. break
  530. num += 1
  531. self.bind(prefix, namespace)
  532. self.__cache[uri] = (prefix, namespace, name)
  533. return self.__cache[uri]
  534. def compute_qname_strict(
  535. self, uri: str, generate: bool = True
  536. ) -> Tuple[str, str, str]:
  537. # code repeated to avoid branching on strict every time
  538. # if output needs to be strict (e.g. for xml) then
  539. # only the strict output should bear the overhead
  540. namespace: str
  541. prefix: Optional[str]
  542. prefix, namespace, name = self.compute_qname(uri, generate)
  543. if is_ncname(str(name)):
  544. return prefix, namespace, name
  545. else:
  546. if uri not in self.__cache_strict:
  547. try:
  548. namespace, name = split_uri(uri, NAME_START_CATEGORIES)
  549. except ValueError:
  550. message = (
  551. "This graph cannot be serialized to a strict format "
  552. "because there is no valid way to shorten {}".format(uri)
  553. )
  554. raise ValueError(message)
  555. # omitted for strict since NCNames cannot be empty
  556. # namespace = URIRef(uri)
  557. # prefix = self.store.prefix(namespace)
  558. # if not prefix:
  559. # raise e
  560. if namespace not in self.__strie:
  561. insert_strie(self.__strie, self.__trie, namespace)
  562. # omitted for strict
  563. # if self.__strie[namespace]:
  564. # pl_namespace = get_longest_namespace(self.__strie[namespace], uri)
  565. # if pl_namespace is not None:
  566. # namespace = pl_namespace
  567. # name = uri[len(namespace):]
  568. namespace = URIRef(namespace)
  569. prefix = self.store.prefix(
  570. namespace
  571. ) # warning multiple prefixes problem
  572. if prefix is None:
  573. if not generate:
  574. raise KeyError(
  575. "No known prefix for {} and generate=False".format(
  576. namespace
  577. )
  578. )
  579. num = 1
  580. while 1:
  581. prefix = "ns%s" % num
  582. if not self.store.namespace(prefix):
  583. break
  584. num += 1
  585. self.bind(prefix, namespace)
  586. self.__cache_strict[uri] = (prefix, namespace, name)
  587. return self.__cache_strict[uri]
  588. def expand_curie(self, curie: str) -> URIRef:
  589. """
  590. Expand a CURIE of the form <prefix:element>, e.g. "rdf:type"
  591. into its full expression:
  592. >>> import rdflib
  593. >>> g = rdflib.Graph()
  594. >>> g.namespace_manager.expand_curie("rdf:type")
  595. rdflib.term.URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#type')
  596. Raises exception if a namespace is not bound to the prefix.
  597. """
  598. if not type(curie) is str: # noqa: E714
  599. raise TypeError(f"Argument must be a string, not {type(curie).__name__}.")
  600. parts = curie.split(":", 1)
  601. if len(parts) != 2:
  602. raise ValueError(
  603. "Malformed curie argument, format should be e.g. “foaf:name”."
  604. )
  605. ns = self.store.namespace(parts[0])
  606. if ns is not None:
  607. return URIRef(f"{str(ns)}{parts[1]}")
  608. else:
  609. raise ValueError(
  610. f"Prefix \"{curie.split(':')[0]}\" not bound to any namespace."
  611. )
  612. def _store_bind(self, prefix: str, namespace: URIRef, override: bool) -> None:
  613. if not _with_bind_override_fix:
  614. return self.store.bind(prefix, namespace)
  615. try:
  616. return self.store.bind(prefix, namespace, override=override)
  617. except TypeError as error:
  618. if "override" in str(error):
  619. logger.debug(
  620. "caught a TypeError, "
  621. "retrying call to %s.bind without override, "
  622. "see https://github.com/RDFLib/rdflib/issues/1880 for more info",
  623. type(self.store),
  624. exc_info=True,
  625. )
  626. return self.store.bind(prefix, namespace)
  627. def bind(
  628. self,
  629. prefix: Optional[str],
  630. namespace: Any,
  631. override: bool = True,
  632. replace: bool = False,
  633. ) -> None:
  634. """Bind a given namespace to the prefix
  635. If override, rebind, even if the given namespace is already
  636. bound to another prefix.
  637. If replace, replace any existing prefix with the new namespace
  638. """
  639. namespace = URIRef(str(namespace))
  640. # When documenting explain that override only applies in what cases
  641. if prefix is None:
  642. prefix = ""
  643. elif " " in prefix:
  644. raise KeyError("Prefixes may not contain spaces.")
  645. bound_namespace = self.store.namespace(prefix)
  646. # Check if the bound_namespace contains a URI
  647. # and if so convert it into a URIRef for comparison
  648. # This is to prevent duplicate namespaces with the
  649. # same URI
  650. if bound_namespace:
  651. bound_namespace = URIRef(bound_namespace)
  652. if bound_namespace and bound_namespace != namespace:
  653. if replace:
  654. self._store_bind(prefix, namespace, override=override)
  655. insert_trie(self.__trie, str(namespace))
  656. return
  657. # prefix already in use for different namespace
  658. #
  659. # append number to end of prefix until we find one
  660. # that's not in use.
  661. if not prefix:
  662. prefix = "default"
  663. num = 1
  664. while 1:
  665. new_prefix = "%s%s" % (prefix, num)
  666. tnamespace = self.store.namespace(new_prefix)
  667. if tnamespace and namespace == URIRef(tnamespace):
  668. # the prefix is already bound to the correct
  669. # namespace
  670. return
  671. if not self.store.namespace(new_prefix):
  672. break
  673. num += 1
  674. self._store_bind(new_prefix, namespace, override=override)
  675. else:
  676. bound_prefix = self.store.prefix(namespace)
  677. if bound_prefix is None:
  678. self._store_bind(prefix, namespace, override=override)
  679. elif bound_prefix == prefix:
  680. pass # already bound
  681. else:
  682. if override or bound_prefix.startswith("_"): # or a generated prefix
  683. self._store_bind(prefix, namespace, override=override)
  684. insert_trie(self.__trie, str(namespace))
  685. def namespaces(self) -> Iterable[Tuple[str, URIRef]]:
  686. for prefix, namespace in self.store.namespaces():
  687. namespace = URIRef(namespace)
  688. yield prefix, namespace
  689. def absolutize(self, uri: str, defrag: int = 1) -> URIRef:
  690. base = Path.cwd().as_uri()
  691. result = urljoin("%s/" % base, uri, allow_fragments=not defrag)
  692. if defrag:
  693. result = urldefrag(result)[0]
  694. if not defrag:
  695. if uri and uri[-1] == "#" and result[-1] != "#":
  696. result = "%s#" % result
  697. return URIRef(result)
  698. # From: http://www.w3.org/TR/REC-xml#NT-CombiningChar
  699. #
  700. # * Name start characters must have one of the categories Ll, Lu, Lo,
  701. # Lt, Nl.
  702. #
  703. # * Name characters other than Name-start characters must have one of
  704. # the categories Mc, Me, Mn, Lm, or Nd.
  705. #
  706. # * Characters in the compatibility area (i.e. with character code
  707. # greater than #xF900 and less than #xFFFE) are not allowed in XML
  708. # names.
  709. #
  710. # * Characters which have a font or compatibility decomposition
  711. # (i.e. those with a "compatibility formatting tag" in field 5 of the
  712. # database -- marked by field 5 beginning with a "<") are not allowed.
  713. #
  714. # * The following characters are treated as name-start characters rather
  715. # than name characters, because the property file classifies them as
  716. # Alphabetic: [#x02BB-#x02C1], #x0559, #x06E5, #x06E6.
  717. #
  718. # * Characters #x20DD-#x20E0 are excluded (in accordance with Unicode
  719. # 2.0, section 5.14).
  720. #
  721. # * Character #x00B7 is classified as an extender, because the property
  722. # list so identifies it.
  723. #
  724. # * Character #x0387 is added as a name character, because #x00B7 is its
  725. # canonical equivalent.
  726. #
  727. # * Characters ':' and '_' are allowed as name-start characters.
  728. #
  729. # * Characters '-' and '.' are allowed as name characters.
  730. NAME_START_CATEGORIES = ["Ll", "Lu", "Lo", "Lt", "Nl"]
  731. SPLIT_START_CATEGORIES = NAME_START_CATEGORIES + ["Nd"]
  732. NAME_CATEGORIES = NAME_START_CATEGORIES + ["Mc", "Me", "Mn", "Lm", "Nd"]
  733. ALLOWED_NAME_CHARS = ["\u00B7", "\u0387", "-", ".", "_", "%", "(", ")"]
  734. # http://www.w3.org/TR/REC-xml-names/#NT-NCName
  735. # [4] NCName ::= (Letter | '_') (NCNameChar)* /* An XML Name, minus
  736. # the ":" */
  737. # [5] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar
  738. # | Extender
  739. def is_ncname(name: str) -> int:
  740. if name:
  741. first = name[0]
  742. if first == "_" or category(first) in NAME_START_CATEGORIES:
  743. for i in range(1, len(name)):
  744. c = name[i]
  745. if not category(c) in NAME_CATEGORIES: # noqa: E713
  746. if c in ALLOWED_NAME_CHARS:
  747. continue
  748. return 0
  749. # if in compatibility area
  750. # if decomposition(c)!='':
  751. # return 0
  752. return 1
  753. return 0
  754. def split_uri(
  755. uri: str, split_start: List[str] = SPLIT_START_CATEGORIES
  756. ) -> Tuple[str, str]:
  757. if uri.startswith(XMLNS):
  758. return (XMLNS, uri.split(XMLNS)[1])
  759. length = len(uri)
  760. for i in range(0, length):
  761. c = uri[-i - 1]
  762. if not category(c) in NAME_CATEGORIES: # noqa: E713
  763. if c in ALLOWED_NAME_CHARS:
  764. continue
  765. for j in range(-1 - i, length):
  766. if category(uri[j]) in split_start or uri[j] == "_":
  767. # _ prevents early split, roundtrip not generate
  768. ns = uri[:j]
  769. if not ns:
  770. break
  771. ln = uri[j:]
  772. return (ns, ln)
  773. break
  774. raise ValueError("Can't split '{}'".format(uri))
  775. def insert_trie(
  776. trie: Dict[str, Any], value: str
  777. ) -> Dict[str, Any]: # aka get_subtrie_or_insert
  778. """Insert a value into the trie if it is not already contained in the trie.
  779. Return the subtree for the value regardless of whether it is a new value
  780. or not."""
  781. if value in trie:
  782. return trie[value]
  783. multi_check = False
  784. for key in tuple(trie.keys()):
  785. if len(value) > len(key) and value.startswith(key):
  786. return insert_trie(trie[key], value)
  787. elif key.startswith(value): # we know the value is not in the trie
  788. if not multi_check:
  789. trie[value] = {}
  790. multi_check = True # there can be multiple longer existing prefixes
  791. dict_ = trie.pop(
  792. key
  793. ) # does not break strie since key<->dict_ remains unchanged
  794. trie[value][key] = dict_
  795. if value not in trie:
  796. trie[value] = {}
  797. return trie[value]
  798. def insert_strie(strie: Dict[str, Any], trie: Dict[str, Any], value: str) -> None:
  799. if value not in strie:
  800. strie[value] = insert_trie(trie, value)
  801. def get_longest_namespace(trie: Dict[str, Any], value: str) -> Optional[str]:
  802. for key in trie:
  803. if value.startswith(key):
  804. out = get_longest_namespace(trie[key], value)
  805. if out is None:
  806. return key
  807. else:
  808. return out
  809. return None
  810. from rdflib.namespace._BRICK import BRICK
  811. from rdflib.namespace._CSVW import CSVW
  812. from rdflib.namespace._DC import DC
  813. from rdflib.namespace._DCAM import DCAM
  814. from rdflib.namespace._DCAT import DCAT
  815. from rdflib.namespace._DCMITYPE import DCMITYPE
  816. from rdflib.namespace._DCTERMS import DCTERMS
  817. from rdflib.namespace._DOAP import DOAP
  818. from rdflib.namespace._FOAF import FOAF
  819. from rdflib.namespace._GEO import GEO
  820. from rdflib.namespace._ODRL2 import ODRL2
  821. from rdflib.namespace._ORG import ORG
  822. from rdflib.namespace._OWL import OWL
  823. from rdflib.namespace._PROF import PROF
  824. from rdflib.namespace._PROV import PROV
  825. from rdflib.namespace._QB import QB
  826. from rdflib.namespace._RDF import RDF
  827. from rdflib.namespace._RDFS import RDFS
  828. from rdflib.namespace._SDO import SDO
  829. from rdflib.namespace._SH import SH
  830. from rdflib.namespace._SKOS import SKOS
  831. from rdflib.namespace._SOSA import SOSA
  832. from rdflib.namespace._SSN import SSN
  833. from rdflib.namespace._TIME import TIME
  834. from rdflib.namespace._VANN import VANN
  835. from rdflib.namespace._VOID import VOID
  836. from rdflib.namespace._WGS import WGS
  837. from rdflib.namespace._XSD import XSD
  838. # prefixes for the core Namespaces shipped with RDFLib
  839. _NAMESPACE_PREFIXES_CORE = {
  840. "owl": OWL,
  841. "rdf": RDF,
  842. "rdfs": RDFS,
  843. "xsd": XSD,
  844. # Namespace binding for XML - needed for RDF/XML
  845. "xml": XMLNS,
  846. }
  847. # prefixes for all the non-core Namespaces shipped with RDFLib
  848. _NAMESPACE_PREFIXES_RDFLIB = {
  849. "brick": BRICK,
  850. "csvw": CSVW,
  851. "dc": DC,
  852. "dcat": DCAT,
  853. "dcmitype": DCMITYPE,
  854. "dcterms": DCTERMS,
  855. "dcam": DCAM,
  856. "doap": DOAP,
  857. "foaf": FOAF,
  858. "geo": GEO,
  859. "odrl": ODRL2,
  860. "org": ORG,
  861. "prof": PROF,
  862. "prov": PROV,
  863. "qb": QB,
  864. "schema": SDO,
  865. "sh": SH,
  866. "skos": SKOS,
  867. "sosa": SOSA,
  868. "ssn": SSN,
  869. "time": TIME,
  870. "vann": VANN,
  871. "void": VOID,
  872. "wgs": WGS,
  873. }