from __future__ import print_function
from types import ModuleType
import rdflib
from rdflib.term import Variable
from rdflib.graph import ConjunctiveGraph
import wrapt
from .data import DataUser
from .import_contextualizer import ImportContextualizer
from .context_store import ContextStore, RDFContextStore
from .contextualize import (BaseContextualizable,
Contextualizable,
ContextualizableClass,
ContextualizingProxy,
contextualize_metaclass)
from .context_common import CONTEXT_IMPORTS
from yarom.mapper import FCN
from six.moves.urllib.parse import quote
from six import text_type
import six
class ModuleProxy(wrapt.ObjectProxy):
def __init__(self, ctx, *args, **kwargs):
super(ModuleProxy, self).__init__(*args, **kwargs)
self._self_overrides = dict()
self._self_ctx = ctx
def add_attr_override(self, name, override):
self._self_overrides[name] = override
def __getattr__(self, name):
o = self._self_overrides.get(name, None)
if o is not None:
return o
else:
o = super(ModuleProxy, self).__getattr__(name)
if isinstance(o, (BaseContextualizable, ContextualizableClass)):
o = o.contextualize(self._self_ctx)
self._self_overrides[name] = o
return o
Contexts = dict()
[docs]class ContextMeta(ContextualizableClass):
@property
def context(self):
return None
@context.setter
def context(self, v):
pass
def contextualize_class_augment(self, context):
if context is None:
return self
ctxd_meta = contextualize_metaclass(context, self)
res = ctxd_meta(self.__name__, (self,), dict(class_context=context.identifier))
return res
def __call__(self, *args, **kwargs):
o = super(ContextMeta, self).__call__(*args, **kwargs)
Contexts[o.identifier] = o
return o
[docs]class Context(six.with_metaclass(ContextMeta, ImportContextualizer, Contextualizable, DataUser)):
"""
A context. Analogous to an RDF context, with some special sauce
"""
def __init__(self, key=None,
imported=(),
ident=None,
mapper=None,
base_namespace=None,
**kwargs):
super(Context, self).__init__(**kwargs)
if key is not None and ident is not None:
raise Exception("Only one of 'key' or 'ident' can be given to Context")
if key is not None and base_namespace is None:
raise Exception("If 'key' is given, then 'base_namespace' must also be given to Context")
if not isinstance(ident, rdflib.term.URIRef) \
and isinstance(ident, (str, text_type)):
ident = rdflib.term.URIRef(ident)
if not isinstance(base_namespace, rdflib.namespace.Namespace) \
and isinstance(base_namespace, (str, text_type)):
base_namespace = rdflib.namespace.Namespace(base_namespace)
if ident is None and key is not None:
ident = rdflib.URIRef(base_namespace[quote(key)])
if not hasattr(self, 'identifier'):
self.identifier = ident
else:
raise Exception(self)
self._contents = dict()
self._statements = []
self._set_buffer_size = 10000
self._imported_contexts = list(imported)
self._rdf_object = None
self._graph = None
self.mapper = mapper
self.base_namespace = base_namespace
self.tripcnt = 0
self.defcnt = 0
def size(self):
return len(self._contents) + sum(x.size()
for x in self._imported_contexts)
def contents(self):
return (x for x in self._statements)
def clear(self):
self._statements.clear()
def add_import(self, context):
self._imported_contexts.append(context)
def add_statement(self, stmt):
if self.identifier != stmt.context.identifier:
raise ValueError("Cannot add statements from a different context")
self._graph = None
self._statements.append(stmt)
def remove_statement(self, stmt):
self._graph = None
self._statements.remove(stmt)
def add_object(self, o):
pass
def add_objects(self, objects):
pass
@property
def imports(self):
for x in self._imported_contexts:
yield x
def save_imports(self, graph=None):
if graph is None:
try:
graph = self.conf['rdf.graph']
except KeyError:
raise Exception('No graph was given and configuration has no graph')
for ctx in self._imported_contexts:
if self.identifier is not None \
and ctx.identifier is not None \
and not isinstance(ctx.identifier, rdflib.term.BNode):
graph.add((self.identifier,
CONTEXT_IMPORTS,
ctx.identifier))
ctx.save_imports(graph)
return graph
def save_context(self, graph=None, inline_imports=False, autocommit=True, seen=None):
self.tripcnt = 0
self.defcnt = 0
if seen is None:
seen = set([])
if id(self) in seen:
return
seen.add(id(self))
if graph is None:
try:
graph = self.conf['rdf.graph']
except KeyError:
raise Exception('No graph was given and configuration has no graph')
if autocommit and hasattr(graph, 'commit'):
graph.commit()
if inline_imports:
for ctx in self._imported_contexts:
ctx.save_context(graph, inline_imports, False, seen)
self.tripcnt += ctx.tripcnt
self.defcnt += ctx.defcnt
if hasattr(graph, 'bind') and self.mapper is not None:
for c in self.mapper.mapped_classes():
if hasattr(c, 'rdf_namespace'):
graph.bind(c.__name__, c.rdf_namespace)
if isinstance(graph, set):
graph.update(self.contents_triples())
else:
ctx_graph = self.get_target_graph(graph)
ctx_graph.addN((s, p, o, ctx_graph)
for s, p, o
in self.contents_triples()
if not (isinstance(s, Variable) or
isinstance(p, Variable) or
isinstance(o, Variable)))
if autocommit and hasattr(graph, 'commit'):
graph.commit()
def get_target_graph(self, graph):
res = graph
if self.identifier is not None:
if hasattr(graph, 'graph_aware') and graph.graph_aware:
res = graph.graph(self.identifier)
elif hasattr(graph, 'context_aware') and graph.context_aware:
res = graph.get_context(self.identifier)
return res
def contents_triples(self):
for x in self._statements:
self.tripcnt += 1
try:
yield x.to_triple()
except Exception as e:
raise e
def contextualize(self, context):
return ContextualizingProxy(context, self)
@property
def rdf_object(self):
if self._rdf_object is None:
# XXX: This is a hack that works for `Evidence.asserts(a.b(c))`
# since we have to define the Context's RDF type *somewhere*.
# Really though, A context's rdf_object should have its type
# statement defined in some other context which specifically holds
# context metadata.
from PyOpenWorm.contextDataObject import ContextDataObject
self._rdf_object = ContextDataObject.contextualize(self.context)(ident=self.identifier)
return self._rdf_object
def __call__(self, o=None, *args, **kwargs):
"""
Parameters
----------
o : object
The object to contexualize. Defaults to locals()
"""
if o is None:
o = kwargs
elif args:
o = {x.__name__: x for x in [o] + list(args)}
if isinstance(o, ModuleType):
return ModuleProxy(self, o)
elif isinstance(o, dict):
return ContextContextManager(self, o)
elif isinstance(o, BaseContextualizable):
return o.contextualize(self)
elif isinstance(o, ContextualizableClass):
# Yes, you can call contextualize on a class and it'll do the right
# thing, but let's keep it simple here, okay?
return o.contextualize_class(self)
else:
return o
def __str__(self):
return repr(self)
def __repr__(self):
return '{}(ident="{}")'.format(FCN(type(self)), getattr(self, 'identifier', '???'))
def load_graph_from_configured_store(self):
return ConjunctiveGraph(store=RDFContextStore(self))
def rdf_graph(self):
if self._graph is None:
self._graph = self.load_staged_graph()
return self._graph
def load_combined_graph(self):
return ConjunctiveGraph(store=ContextStore(context=self,
include_stored=True))
def load_staged_graph(self):
return ConjunctiveGraph(store=ContextStore(context=self))
@property
def query(self):
return QueryContext(graph=self.load_combined_graph(),
ident=self.identifier)
@property
def staged(self):
return QueryContext(graph=self.load_staged_graph(),
ident=self.identifier)
@property
def stored(self):
return QueryContext(graph=self.load_graph_from_configured_store(),
ident=self.identifier)
[docs]class QueryContext(Context):
def __init__(self, graph, *args, **kwargs):
super(QueryContext, self).__init__(*args, **kwargs)
self.__graph = graph
def rdf_graph(self):
return self.__graph
[docs]class ContextContextManager(object):
""" The context manager created when Context::__call__ is passed a dict """
def __init__(self, ctx, to_import):
self._overrides = dict()
self._ctx = ctx
self._backing_dict = to_import
self.save = self._ctx.save_context
@property
def context(self):
return self._ctx
def __call__(self, o):
return self._ctx(o)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __getattr__(self, name):
return self.lookup(name)
def __getitem__(self, key):
return self.lookup(key)
def lookup(self, key):
o = self._overrides.get(key, None)
if o is not None:
return o
else:
o = self._backing_dict[key]
if isinstance(o, (BaseContextualizable, ContextualizableClass)):
o = o.contextualize(self._ctx)
self._overrides[key] = o
return o