memory.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. #
  2. #
  3. from __future__ import annotations
  4. from typing import (
  5. TYPE_CHECKING,
  6. Any,
  7. Collection,
  8. Dict,
  9. Generator,
  10. Iterator,
  11. Mapping,
  12. Optional,
  13. Set,
  14. Tuple,
  15. Union,
  16. overload,
  17. )
  18. from rdflib.store import Store
  19. from rdflib.util import _coalesce
  20. if TYPE_CHECKING:
  21. from rdflib.graph import (
  22. Graph,
  23. _ContextType,
  24. _ObjectType,
  25. _PredicateType,
  26. _SubjectType,
  27. _TriplePatternType,
  28. _TripleType,
  29. )
  30. from rdflib.plugins.sparql.sparql import Query, Update
  31. from rdflib.query import Result
  32. from rdflib.term import Identifier, URIRef
  33. __all__ = ["SimpleMemory", "Memory"]
  34. ANY: None = None
  35. class SimpleMemory(Store):
  36. """A fast naive in memory implementation of a triple store.
  37. This triple store uses nested dictionaries to store triples. Each
  38. triple is stored in two such indices as follows `spo[s][p][o]` = 1 and
  39. `pos[p][o][s]` = 1.
  40. Authors: Michel Pelletier, Daniel Krech, Stefan Niederhauser
  41. """
  42. def __init__(
  43. self,
  44. configuration: Optional[str] = None,
  45. identifier: Optional[Identifier] = None,
  46. ):
  47. super(SimpleMemory, self).__init__(configuration)
  48. self.identifier = identifier
  49. # indexed by [subject][predicate][object]
  50. self.__spo: Dict[_SubjectType, Dict[_PredicateType, Dict[_ObjectType, int]]] = (
  51. {}
  52. )
  53. # indexed by [predicate][object][subject]
  54. self.__pos: Dict[_PredicateType, Dict[_ObjectType, Dict[_SubjectType, int]]] = (
  55. {}
  56. )
  57. # indexed by [predicate][object][subject]
  58. self.__osp: Dict[_ObjectType, Dict[_SubjectType, Dict[_PredicateType, int]]] = (
  59. {}
  60. )
  61. self.__namespace: Dict[str, URIRef] = {}
  62. self.__prefix: Dict[URIRef, str] = {}
  63. def add(
  64. self,
  65. triple: _TripleType,
  66. context: _ContextType,
  67. quoted: bool = False,
  68. ) -> None:
  69. """Add a triple to the store of triples."""
  70. # add dictionary entries for spo[s][p][p] = 1 and pos[p][o][s]
  71. # = 1, creating the nested dictionaries where they do not yet
  72. # exits.
  73. subject, predicate, object = triple
  74. spo = self.__spo
  75. try:
  76. po = spo[subject]
  77. except: # noqa: E722
  78. po = spo[subject] = {}
  79. try:
  80. o = po[predicate]
  81. except: # noqa: E722
  82. o = po[predicate] = {}
  83. o[object] = 1
  84. pos = self.__pos
  85. try:
  86. os = pos[predicate]
  87. except: # noqa: E722
  88. os = pos[predicate] = {}
  89. try:
  90. s = os[object]
  91. except: # noqa: E722
  92. s = os[object] = {}
  93. s[subject] = 1
  94. osp = self.__osp
  95. try:
  96. sp = osp[object]
  97. except: # noqa: E722
  98. sp = osp[object] = {}
  99. try:
  100. p = sp[subject]
  101. except: # noqa: E722
  102. p = sp[subject] = {}
  103. p[predicate] = 1
  104. def remove(
  105. self,
  106. triple_pattern: _TriplePatternType,
  107. context: Optional[_ContextType] = None,
  108. ) -> None:
  109. for (subject, predicate, object), c in list(self.triples(triple_pattern)):
  110. del self.__spo[subject][predicate][object]
  111. del self.__pos[predicate][object][subject]
  112. del self.__osp[object][subject][predicate]
  113. def triples(
  114. self,
  115. triple_pattern: _TriplePatternType,
  116. context: Optional[_ContextType] = None,
  117. ) -> Iterator[Tuple[_TripleType, Iterator[Optional[_ContextType]]]]:
  118. """A generator over all the triples matching"""
  119. subject, predicate, object = triple_pattern
  120. if subject != ANY: # subject is given
  121. spo = self.__spo
  122. if subject in spo:
  123. subjectDictionary = spo[subject] # noqa: N806
  124. if predicate != ANY: # subject+predicate is given
  125. if predicate in subjectDictionary:
  126. if object != ANY: # subject+predicate+object is given
  127. if object in subjectDictionary[predicate]:
  128. yield (subject, predicate, object), self.__contexts()
  129. else: # given object not found
  130. pass
  131. else: # subject+predicate is given, object unbound
  132. for o in subjectDictionary[predicate].keys():
  133. yield (subject, predicate, o), self.__contexts()
  134. else: # given predicate not found
  135. pass
  136. else: # subject given, predicate unbound
  137. for p in subjectDictionary.keys():
  138. if object != ANY: # object is given
  139. if object in subjectDictionary[p]:
  140. yield (subject, p, object), self.__contexts()
  141. else: # given object not found
  142. pass
  143. else: # object unbound
  144. for o in subjectDictionary[p].keys():
  145. yield (subject, p, o), self.__contexts()
  146. else: # given subject not found
  147. pass
  148. elif predicate != ANY: # predicate is given, subject unbound
  149. pos = self.__pos
  150. if predicate in pos:
  151. predicateDictionary = pos[predicate] # noqa: N806
  152. if object != ANY: # predicate+object is given, subject unbound
  153. if object in predicateDictionary:
  154. for s in predicateDictionary[object].keys():
  155. yield (s, predicate, object), self.__contexts()
  156. else: # given object not found
  157. pass
  158. else: # predicate is given, object+subject unbound
  159. for o in predicateDictionary.keys():
  160. for s in predicateDictionary[o].keys():
  161. yield (s, predicate, o), self.__contexts()
  162. elif object != ANY: # object is given, subject+predicate unbound
  163. osp = self.__osp
  164. if object in osp:
  165. objectDictionary = osp[object] # noqa: N806
  166. for s in objectDictionary.keys():
  167. for p in objectDictionary[s].keys():
  168. yield (s, p, object), self.__contexts()
  169. else: # subject+predicate+object unbound
  170. spo = self.__spo
  171. for s in spo.keys():
  172. subjectDictionary = spo[s] # noqa: N806
  173. for p in subjectDictionary.keys():
  174. for o in subjectDictionary[p].keys():
  175. yield (s, p, o), self.__contexts()
  176. def __len__(self, context: Optional[_ContextType] = None) -> int:
  177. # @@ optimize
  178. i = 0
  179. for triple in self.triples((None, None, None)):
  180. i += 1
  181. return i
  182. def bind(self, prefix: str, namespace: URIRef, override: bool = True) -> None:
  183. # should be identical to `Memory.bind`
  184. bound_namespace = self.__namespace.get(prefix)
  185. bound_prefix = _coalesce(
  186. self.__prefix.get(namespace),
  187. # type error: error: Argument 1 to "get" of "Mapping" has incompatible type "Optional[URIRef]"; expected "URIRef"
  188. self.__prefix.get(bound_namespace), # type: ignore[arg-type]
  189. )
  190. if override:
  191. if bound_prefix is not None:
  192. del self.__namespace[bound_prefix]
  193. if bound_namespace is not None:
  194. del self.__prefix[bound_namespace]
  195. self.__prefix[namespace] = prefix
  196. self.__namespace[prefix] = namespace
  197. else:
  198. # type error: Invalid index type "Optional[URIRef]" for "Dict[URIRef, str]"; expected type "URIRef"
  199. self.__prefix[_coalesce(bound_namespace, namespace)] = _coalesce( # type: ignore[index]
  200. bound_prefix, default=prefix
  201. )
  202. # type error: Invalid index type "Optional[str]" for "Dict[str, URIRef]"; expected type "str"
  203. self.__namespace[_coalesce(bound_prefix, prefix)] = _coalesce( # type: ignore[index]
  204. bound_namespace, default=namespace
  205. )
  206. def namespace(self, prefix: str) -> Optional[URIRef]:
  207. return self.__namespace.get(prefix, None)
  208. def prefix(self, namespace: URIRef) -> Optional[str]:
  209. return self.__prefix.get(namespace, None)
  210. def namespaces(self) -> Iterator[Tuple[str, URIRef]]:
  211. for prefix, namespace in self.__namespace.items():
  212. yield prefix, namespace
  213. def __contexts(self) -> Generator[_ContextType, None, None]:
  214. # TODO: best way to return empty generator
  215. # type error: Need type annotation for "c"
  216. return (c for c in []) # type: ignore[var-annotated]
  217. # type error: Missing return statement
  218. def query( # type: ignore[return]
  219. self,
  220. query: Union[Query, str],
  221. initNs: Mapping[str, Any], # noqa: N803
  222. initBindings: Mapping[str, Identifier], # noqa: N803
  223. queryGraph: str, # noqa: N803
  224. **kwargs: Any,
  225. ) -> Result:
  226. super(SimpleMemory, self).query(
  227. query, initNs, initBindings, queryGraph, **kwargs
  228. )
  229. def update(
  230. self,
  231. update: Union[Update, str],
  232. initNs: Mapping[str, Any], # noqa: N803
  233. initBindings: Mapping[str, Identifier], # noqa: N803
  234. queryGraph: str, # noqa: N803
  235. **kwargs: Any,
  236. ) -> None:
  237. super(SimpleMemory, self).update(
  238. update, initNs, initBindings, queryGraph, **kwargs
  239. )
  240. class Memory(Store):
  241. """An in memory implementation of a triple store.
  242. Same as SimpleMemory above, but is Context-aware, Graph-aware, and Formula-aware
  243. Authors: Ashley Sommer
  244. """
  245. context_aware = True
  246. formula_aware = True
  247. graph_aware = True
  248. def __init__(
  249. self,
  250. configuration: Optional[str] = None,
  251. identifier: Optional[Identifier] = None,
  252. ):
  253. super(Memory, self).__init__(configuration)
  254. self.identifier = identifier
  255. # indexed by [subject][predicate][object]
  256. self.__spo: Dict[_SubjectType, Dict[_PredicateType, Dict[_ObjectType, int]]] = (
  257. {}
  258. )
  259. # indexed by [predicate][object][subject]
  260. self.__pos: Dict[_PredicateType, Dict[_ObjectType, Dict[_SubjectType, int]]] = (
  261. {}
  262. )
  263. # indexed by [predicate][object][subject]
  264. self.__osp: Dict[_ObjectType, Dict[_SubjectType, Dict[_PredicateType, int]]] = (
  265. {}
  266. )
  267. self.__namespace: Dict[str, URIRef] = {}
  268. self.__prefix: Dict[URIRef, str] = {}
  269. self.__context_obj_map: Dict[str, Graph] = {}
  270. self.__tripleContexts: Dict[_TripleType, Dict[Optional[str], bool]] = {}
  271. self.__contextTriples: Dict[Optional[str], Set[_TripleType]] = {None: set()}
  272. # all contexts used in store (unencoded)
  273. self.__all_contexts: Set[Graph] = set()
  274. # default context information for triples
  275. self.__defaultContexts: Optional[Dict[Optional[str], bool]] = None
  276. def add(
  277. self,
  278. triple: _TripleType,
  279. context: _ContextType,
  280. quoted: bool = False,
  281. ) -> None:
  282. """Add a triple to the store of triples."""
  283. # add dictionary entries for spo[s][p][p] = 1 and pos[p][o][s]
  284. # = 1, creating the nested dictionaries where they do not yet
  285. # exits.
  286. Store.add(self, triple, context, quoted=quoted)
  287. if context is not None:
  288. self.__all_contexts.add(context)
  289. subject, predicate, object_ = triple
  290. spo = self.__spo
  291. try:
  292. po = spo[subject]
  293. except LookupError:
  294. po = spo[subject] = {}
  295. try:
  296. o = po[predicate]
  297. except LookupError:
  298. o = po[predicate] = {}
  299. try:
  300. _ = o[object_]
  301. # This cannot be reached if (s, p, o) was not inserted before.
  302. triple_exists = True
  303. except KeyError:
  304. o[object_] = 1
  305. triple_exists = False
  306. self.__add_triple_context(triple, triple_exists, context, quoted)
  307. if triple_exists:
  308. # No need to insert twice this triple.
  309. return
  310. pos = self.__pos
  311. try:
  312. os = pos[predicate]
  313. except LookupError:
  314. os = pos[predicate] = {}
  315. try:
  316. s = os[object_]
  317. except LookupError:
  318. s = os[object_] = {}
  319. s[subject] = 1
  320. osp = self.__osp
  321. try:
  322. sp = osp[object_]
  323. except LookupError:
  324. sp = osp[object_] = {}
  325. try:
  326. p = sp[subject]
  327. except LookupError:
  328. p = sp[subject] = {}
  329. p[predicate] = 1
  330. def remove(
  331. self,
  332. triple_pattern: _TriplePatternType,
  333. context: Optional[_ContextType] = None,
  334. ) -> None:
  335. req_ctx = self.__ctx_to_str(context)
  336. for triple, c in self.triples(triple_pattern, context=context):
  337. subject, predicate, object_ = triple
  338. for ctx in self.__get_context_for_triple(triple):
  339. if context is not None and req_ctx != ctx:
  340. continue
  341. self.__remove_triple_context(triple, ctx)
  342. ctxs = self.__get_context_for_triple(triple, skipQuoted=True)
  343. if None in ctxs and (context is None or len(ctxs) == 1):
  344. # remove from default graph too
  345. self.__remove_triple_context(triple, None)
  346. if len(self.__get_context_for_triple(triple)) == 0:
  347. del self.__spo[subject][predicate][object_]
  348. del self.__pos[predicate][object_][subject]
  349. del self.__osp[object_][subject][predicate]
  350. del self.__tripleContexts[triple]
  351. if (
  352. req_ctx is not None
  353. and req_ctx in self.__contextTriples
  354. and len(self.__contextTriples[req_ctx]) == 0
  355. ):
  356. # all triples are removed out of this context
  357. # and it's not the default context so delete it
  358. del self.__contextTriples[req_ctx]
  359. if (
  360. triple_pattern == (None, None, None)
  361. and context in self.__all_contexts
  362. and not self.graph_aware
  363. ):
  364. # remove the whole context
  365. self.__all_contexts.remove(context)
  366. def triples(
  367. self,
  368. triple_pattern: _TriplePatternType,
  369. context: Optional[_ContextType] = None,
  370. ) -> Generator[
  371. Tuple[_TripleType, Generator[Optional[_ContextType], None, None]],
  372. None,
  373. None,
  374. ]:
  375. """A generator over all the triples matching"""
  376. req_ctx = self.__ctx_to_str(context)
  377. subject, predicate, object_ = triple_pattern
  378. # all triples case (no triple parts given as pattern)
  379. if subject is None and predicate is None and object_ is None:
  380. # Just dump all known triples from the given graph
  381. if req_ctx not in self.__contextTriples:
  382. return
  383. for triple in self.__contextTriples[req_ctx].copy():
  384. yield triple, self.__contexts(triple)
  385. # optimize "triple in graph" case (all parts given)
  386. elif subject is not None and predicate is not None and object_ is not None:
  387. # type error: Incompatible types in assignment (expression has type "Tuple[Optional[IdentifiedNode], Optional[IdentifiedNode], Optional[Identifier]]", variable has type "Tuple[IdentifiedNode, IdentifiedNode, Identifier]")
  388. # NOTE on type error: at this point, all elements of triple_pattern
  389. # is not None, so it has the same type as triple
  390. triple = triple_pattern # type: ignore[assignment]
  391. try:
  392. _ = self.__spo[subject][predicate][object_]
  393. if self.__triple_has_context(triple, req_ctx):
  394. yield triple, self.__contexts(triple)
  395. except KeyError:
  396. return
  397. elif subject is not None: # subject is given
  398. spo = self.__spo
  399. if subject in spo:
  400. subjectDictionary = spo[subject] # noqa: N806
  401. if predicate is not None: # subject+predicate is given
  402. if predicate in subjectDictionary:
  403. if object_ is not None: # subject+predicate+object is given
  404. if object_ in subjectDictionary[predicate]:
  405. triple = (subject, predicate, object_)
  406. if self.__triple_has_context(triple, req_ctx):
  407. yield triple, self.__contexts(triple)
  408. else: # given object not found
  409. pass
  410. else: # subject+predicate is given, object unbound
  411. for o in list(subjectDictionary[predicate].keys()):
  412. triple = (subject, predicate, o)
  413. if self.__triple_has_context(triple, req_ctx):
  414. yield triple, self.__contexts(triple)
  415. else: # given predicate not found
  416. pass
  417. else: # subject given, predicate unbound
  418. for p in list(subjectDictionary.keys()):
  419. if object_ is not None: # object is given
  420. if object_ in subjectDictionary[p]:
  421. triple = (subject, p, object_)
  422. if self.__triple_has_context(triple, req_ctx):
  423. yield triple, self.__contexts(triple)
  424. else: # given object not found
  425. pass
  426. else: # object unbound
  427. for o in list(subjectDictionary[p].keys()):
  428. triple = (subject, p, o)
  429. if self.__triple_has_context(triple, req_ctx):
  430. yield triple, self.__contexts(triple)
  431. else: # given subject not found
  432. pass
  433. elif predicate is not None: # predicate is given, subject unbound
  434. pos = self.__pos
  435. if predicate in pos:
  436. predicateDictionary = pos[predicate] # noqa: N806
  437. if object_ is not None: # predicate+object is given, subject unbound
  438. if object_ in predicateDictionary:
  439. for s in list(predicateDictionary[object_].keys()):
  440. triple = (s, predicate, object_)
  441. if self.__triple_has_context(triple, req_ctx):
  442. yield triple, self.__contexts(triple)
  443. else: # given object not found
  444. pass
  445. else: # predicate is given, object+subject unbound
  446. for o in list(predicateDictionary.keys()):
  447. for s in list(predicateDictionary[o].keys()):
  448. triple = (s, predicate, o)
  449. if self.__triple_has_context(triple, req_ctx):
  450. yield triple, self.__contexts(triple)
  451. elif object_ is not None: # object is given, subject+predicate unbound
  452. osp = self.__osp
  453. if object_ in osp:
  454. objectDictionary = osp[object_] # noqa: N806
  455. for s in list(objectDictionary.keys()):
  456. for p in list(objectDictionary[s].keys()):
  457. triple = (s, p, object_)
  458. if self.__triple_has_context(triple, req_ctx):
  459. yield triple, self.__contexts(triple)
  460. else: # subject+predicate+object unbound
  461. # Shouldn't get here if all other cases above worked correctly.
  462. spo = self.__spo
  463. for s in list(spo.keys()):
  464. subjectDictionary = spo[s] # noqa: N806
  465. for p in list(subjectDictionary.keys()):
  466. for o in list(subjectDictionary[p].keys()):
  467. triple = (s, p, o)
  468. if self.__triple_has_context(triple, req_ctx):
  469. yield triple, self.__contexts(triple)
  470. def bind(self, prefix: str, namespace: URIRef, override: bool = True) -> None:
  471. # should be identical to `SimpleMemory.bind`
  472. bound_namespace = self.__namespace.get(prefix)
  473. bound_prefix = _coalesce(
  474. self.__prefix.get(namespace),
  475. # type error: error: Argument 1 to "get" of "Mapping" has incompatible type "Optional[URIRef]"; expected "URIRef"
  476. self.__prefix.get(bound_namespace), # type: ignore[arg-type]
  477. )
  478. if override:
  479. if bound_prefix is not None:
  480. del self.__namespace[bound_prefix]
  481. if bound_namespace is not None:
  482. del self.__prefix[bound_namespace]
  483. self.__prefix[namespace] = prefix
  484. self.__namespace[prefix] = namespace
  485. else:
  486. # type error: Invalid index type "Optional[URIRef]" for "Dict[URIRef, str]"; expected type "URIRef"
  487. self.__prefix[_coalesce(bound_namespace, namespace)] = _coalesce( # type: ignore[index]
  488. bound_prefix, default=prefix
  489. )
  490. # type error: Invalid index type "Optional[str]" for "Dict[str, URIRef]"; expected type "str"
  491. # type error: Incompatible types in assignment (expression has type "Optional[URIRef]", target has type "URIRef")
  492. self.__namespace[_coalesce(bound_prefix, prefix)] = _coalesce( # type: ignore[index]
  493. bound_namespace, default=namespace
  494. )
  495. def namespace(self, prefix: str) -> Optional[URIRef]:
  496. return self.__namespace.get(prefix, None)
  497. def prefix(self, namespace: URIRef) -> Optional[str]:
  498. return self.__prefix.get(namespace, None)
  499. def namespaces(self) -> Iterator[Tuple[str, URIRef]]:
  500. for prefix, namespace in self.__namespace.items():
  501. yield prefix, namespace
  502. def contexts(
  503. self, triple: Optional[_TripleType] = None
  504. ) -> Generator[_ContextType, None, None]:
  505. if triple is None or triple == (None, None, None):
  506. return (context for context in list(self.__all_contexts))
  507. subj, pred, obj = triple
  508. try:
  509. _ = self.__spo[subj][pred][obj]
  510. return self.__contexts(triple)
  511. except KeyError:
  512. return (_ for _ in [])
  513. def __len__(self, context: Optional[_ContextType] = None) -> int:
  514. ctx = self.__ctx_to_str(context)
  515. if ctx not in self.__contextTriples:
  516. return 0
  517. return len(self.__contextTriples[ctx])
  518. def add_graph(self, graph: Graph) -> None:
  519. if not self.graph_aware:
  520. Store.add_graph(self, graph)
  521. else:
  522. self.__all_contexts.add(graph)
  523. def remove_graph(self, graph: Graph) -> None:
  524. if not self.graph_aware:
  525. Store.remove_graph(self, graph)
  526. else:
  527. self.remove((None, None, None), graph)
  528. try:
  529. self.__all_contexts.remove(graph)
  530. except KeyError:
  531. pass # we didn't know this graph, no problem
  532. # internal utility methods below
  533. def __add_triple_context(
  534. self,
  535. triple: _TripleType,
  536. triple_exists: bool,
  537. context: Optional[_ContextType],
  538. quoted: bool,
  539. ) -> None:
  540. """add the given context to the set of contexts for the triple"""
  541. ctx = self.__ctx_to_str(context)
  542. quoted = bool(quoted)
  543. if triple_exists:
  544. # we know the triple exists somewhere in the store
  545. try:
  546. triple_context = self.__tripleContexts[triple]
  547. except KeyError:
  548. # triple exists with default ctx info
  549. # start with a copy of the default ctx info
  550. # type error: Item "None" of "Optional[Dict[Optional[str], bool]]" has no attribute "copy"
  551. triple_context = self.__tripleContexts[triple] = (
  552. self.__defaultContexts.copy() # type: ignore[union-attr]
  553. )
  554. triple_context[ctx] = quoted
  555. if not quoted:
  556. triple_context[None] = quoted
  557. else:
  558. # the triple didn't exist before in the store
  559. if quoted: # this context only
  560. triple_context = self.__tripleContexts[triple] = {ctx: quoted}
  561. else: # default context as well
  562. triple_context = self.__tripleContexts[triple] = {
  563. ctx: quoted,
  564. None: quoted,
  565. }
  566. # if the triple is not quoted add it to the default context
  567. if not quoted:
  568. self.__contextTriples[None].add(triple)
  569. # always add the triple to given context, making sure it's initialized
  570. if ctx not in self.__contextTriples:
  571. self.__contextTriples[ctx] = set()
  572. self.__contextTriples[ctx].add(triple)
  573. # if this is the first ever triple in the store, set default ctx info
  574. if self.__defaultContexts is None:
  575. self.__defaultContexts = triple_context
  576. # if the context info is the same as default, no need to store it
  577. if triple_context == self.__defaultContexts:
  578. del self.__tripleContexts[triple]
  579. def __get_context_for_triple(
  580. self, triple: _TripleType, skipQuoted: bool = False # noqa: N803
  581. ) -> Collection[Optional[str]]:
  582. """return a list of contexts (str) for the triple, skipping
  583. quoted contexts if skipQuoted==True"""
  584. ctxs = self.__tripleContexts.get(triple, self.__defaultContexts)
  585. if not skipQuoted:
  586. # type error: Item "None" of "Optional[Dict[Optional[str], bool]]" has no attribute "keys"
  587. return ctxs.keys() # type: ignore[union-attr]
  588. # type error: Item "None" of "Optional[Dict[Optional[str], bool]]" has no attribute "items"
  589. return [ctx for ctx, quoted in ctxs.items() if not quoted] # type: ignore[union-attr]
  590. def __triple_has_context(self, triple: _TripleType, ctx: Optional[str]) -> bool:
  591. """return True if the triple exists in the given context"""
  592. # type error: Unsupported right operand type for in ("Optional[Dict[Optional[str], bool]]")
  593. return ctx in self.__tripleContexts.get(triple, self.__defaultContexts) # type: ignore[operator]
  594. def __remove_triple_context(self, triple: _TripleType, ctx):
  595. """remove the context from the triple"""
  596. # type error: Item "None" of "Optional[Dict[Optional[str], bool]]" has no attribute "copy"
  597. ctxs = self.__tripleContexts.get(triple, self.__defaultContexts).copy() # type: ignore[union-attr]
  598. del ctxs[ctx]
  599. if ctxs == self.__defaultContexts:
  600. del self.__tripleContexts[triple]
  601. else:
  602. self.__tripleContexts[triple] = ctxs
  603. self.__contextTriples[ctx].remove(triple)
  604. @overload
  605. def __ctx_to_str(self, ctx: _ContextType) -> str: ...
  606. @overload
  607. def __ctx_to_str(self, ctx: None) -> None: ...
  608. def __ctx_to_str(self, ctx: Optional[_ContextType]) -> Optional[str]:
  609. if ctx is None:
  610. return None
  611. try:
  612. # ctx could be a graph. In that case, use its identifier
  613. ctx_str = "{}:{}".format(ctx.identifier.__class__.__name__, ctx.identifier)
  614. self.__context_obj_map[ctx_str] = ctx
  615. return ctx_str
  616. except AttributeError:
  617. # otherwise, ctx should be a URIRef or BNode or str
  618. # NOTE on type errors: This is actually never called with ctx value as str in all unit tests, so this seems like it should just not be here.
  619. # type error: Subclass of "Graph" and "str" cannot exist: would have incompatible method signatures
  620. if isinstance(ctx, str): # type: ignore[unreachable]
  621. # type error: Statement is unreachable
  622. ctx_str = "{}:{}".format(ctx.__class__.__name__, ctx) # type: ignore[unreachable]
  623. if ctx_str in self.__context_obj_map:
  624. return ctx_str
  625. self.__context_obj_map[ctx_str] = ctx
  626. return ctx_str
  627. raise RuntimeError("Cannot use that type of object as a Graph context")
  628. def __contexts(self, triple: _TripleType) -> Generator[_ContextType, None, None]:
  629. """return a generator for all the non-quoted contexts
  630. (dereferenced) the encoded triple appears in"""
  631. # type error: Argument 2 to "get" of "Mapping" has incompatible type "str"; expected "Optional[Graph]"
  632. return (
  633. self.__context_obj_map.get(ctx_str, ctx_str) # type: ignore[arg-type]
  634. for ctx_str in self.__get_context_for_triple(triple, skipQuoted=True)
  635. if ctx_str is not None
  636. )
  637. # type error: Missing return statement
  638. def query( # type: ignore[return]
  639. self,
  640. query: Union[Query, str],
  641. initNs: Mapping[str, Any], # noqa: N803
  642. initBindings: Mapping[str, Identifier], # noqa: N803
  643. queryGraph: str, # noqa: N803
  644. **kwargs,
  645. ) -> Result:
  646. super(Memory, self).query(query, initNs, initBindings, queryGraph, **kwargs)
  647. def update(
  648. self,
  649. update: Union[Update, Any],
  650. initNs: Mapping[str, Any], # noqa: N803
  651. initBindings: Mapping[str, Identifier], # noqa: N803
  652. queryGraph: str, # noqa: N803
  653. **kwargs,
  654. ) -> None:
  655. super(Memory, self).update(update, initNs, initBindings, queryGraph, **kwargs)