from itertools import chain
from rdflib.store import Store, VALID_STORE, NO_STORE
from rdflib.plugins.memory import IOMemory
from rdflib.term import Variable
from yarom.rdfUtils import transitive_lookup
from .context_common import CONTEXT_IMPORTS
[docs]class ContextStoreException(Exception):
pass
class ContextStore(Store):
context_aware = True
def __init__(self, context=None, include_stored=False, **kwargs):
"""
Parameters
----------
context : PyOpenWorm.context.Context
context
"""
super(ContextStore, self).__init__(**kwargs)
self._memory_store = None
self._include_stored = include_stored
if context is not None:
self._init_store(context)
def open(self, configuration, create=False):
if self.ctx is not None:
return VALID_STORE
else:
return NO_STORE
def _init_store(self, ctx):
self.ctx = ctx
if self._include_stored:
self._store_store = RDFContextStore(ctx)
else:
self._store_store = None
if self._memory_store is None:
self._memory_store = IOMemory()
self._init_store0(ctx)
def _init_store0(self, ctx, seen=None):
if seen is None:
seen = set()
ctxid = ctx.identifier
if ctxid in seen:
return
seen.add(ctxid)
self._memory_store.addN((s, p, o, ctxid)
for s, p, o
in ctx.contents_triples()
if not (isinstance(s, Variable) or
isinstance(p, Variable) or
isinstance(o, Variable)))
for cctx in ctx.imports:
self._init_store0(cctx, seen)
def close(self, commit_pending_transaction=False):
self.ctx = None
self._memory_store = None
# RDF APIs
def add(self, triple, context, quoted=False):
raise NotImplementedError("This is a query-only store")
def addN(self, quads):
raise NotImplementedError("This is a query-only store")
def remove(self, triple, context=None):
raise NotImplementedError("This is a query-only store")
def triples(self, triple_pattern, context=None):
if self._memory_store is None:
raise ContextStoreException("Database has not been opened")
context = getattr(context, 'identifier', context)
context_triples = []
if self._store_store is not None:
context_triples.append(self._store_store.triples(triple_pattern,
context))
return chain(self._memory_store.triples(triple_pattern, context),
*context_triples)
def __len__(self, context=None):
"""
Number of statements in the store. This should only account for non-
quoted (asserted) statements if the context is not specified,
otherwise it should return the number of statements in the formula or
context given.
:param context: a graph instance to query or None
"""
if self._memory_store is None:
raise ContextStoreException("Database has not been opened")
if self._store_store is None:
return len(self._memory_store)
else:
# We don't know which triples may overlap, so we can't return an accurate count without doing something
# expensive, so we just give up
raise NotImplementedError()
def contexts(self, triple=None):
"""
Generator over all contexts in the graph. If triple is specified,
a generator over all contexts the triple is in.
if store is graph_aware, may also return empty contexts
:returns: a generator over Nodes
"""
if self._memory_store is None:
raise ContextStoreException("Database has not been opened")
seen = set()
rest = ()
if self._store_store is not None:
rest = self._store_store.contexts(triple)
for ctx in chain(self._memory_store.contexts(triple), rest):
if ctx in seen:
continue
seen.add(ctx)
yield ctx
class RDFContextStore(Store):
# Returns triples imported by the given context
context_aware = True
def __init__(self, context=None, imports_graph=None, **kwargs):
super(RDFContextStore, self).__init__(**kwargs)
self.__graph = context.rdf
self.__imports_graph = imports_graph
self.__store = self.__graph.store
self.__context = context
self.__context_transitive_imports = None
def __init_contexts(self):
if self.__store is not None and self.__context_transitive_imports is None:
imports = transitive_lookup(self.__store, self.__context.identifier, CONTEXT_IMPORTS, self.__imports_graph)
self.__context_transitive_imports = imports
def triples(self, pattern, context=None):
self.__init_contexts()
for t in self.__store.triples(pattern, context):
contexts = set(getattr(c, 'identifier', c) for c in t[1])
if self.__context_transitive_imports:
inter = self.__context_transitive_imports & contexts
else:
# Note that our own identifier is also included in the
# transitive imports, so if we don't have *any* imports then we
# fall back to querying across all contexts => we don't filter
# based on contexts. This is in line with rdflib ConjuctiveGraph
# semantics
inter = contexts
if inter:
yield t[0], inter
def contexts(self, triple=None):
if triple is not None:
for x in self.triples(triple):
for c in x[1]:
yield getattr(c, 'identifier', c)
else:
self.__init_contexts()
for c in self.__context_transitive_imports:
yield c
def namespace(self, prefix):
return self.__store.namespace(prefix)
def prefix(self, uri):
return self.__store.prefix(uri)
def bind(self, prefix, namespace):
return self.__store.bind(prefix, namespace)
def namespaces(self):
for x in self.__store.namespaces():
yield x