Source code for PyOpenWorm.context

from __future__ import print_function
from types import ModuleType
import rdflib
from rdflib.term import Variable, URIRef
from rdflib.graph import ConjunctiveGraph
import wrapt
from . import BASE_MAPPER
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 yarom.utils import FCN
from six.moves.urllib.parse import quote
from six import text_type
import six


DATA_CONTEXT_KEY = 'data_context_id'
IMPORTS_CONTEXT_KEY = 'imports_context_id'


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


[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) return ctxd_meta(self.__name__, (self,), dict(class_context=context.identifier))
[docs]class ContextualizableDataUserMixin(Contextualizable, DataUser): @property def conf(self): if self.context is None: return super(ContextualizableDataUserMixin, self).conf else: return self.context.conf @conf.setter def conf(self, conf): super(ContextualizableDataUserMixin, self).conf = conf
[docs]class Context(six.with_metaclass(ContextMeta, ImportContextualizer, ContextualizableDataUserMixin)): """ 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, URIRef) \ and isinstance(ident, (str, text_type)): ident = 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 = URIRef(base_namespace[quote(key)]) if not hasattr(self, 'identifier'): self.identifier = ident else: raise Exception(self) self._statements = [] self._imported_contexts = list(imported) self._rdf_object = None self._graph = None if mapper is None: mapper = self.conf.get('mapper', BASE_MAPPER) self.mapper = mapper self.base_namespace = base_namespace self._change_counter = 0 self._triples_saved = 0 def contents(self): return (x for x in self._statements) def clear(self): del self._statements[:] 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) self._change_counter += 1 def remove_statement(self, stmt): self._graph = None self._statements.remove(stmt) self._change_counter += 1 @property def imports(self): for x in self._imported_contexts: yield x def save_imports(self, context=None, *args, **kwargs): if not context: ctx_key = self.conf[IMPORTS_CONTEXT_KEY] context = Context(ident=ctx_key, conf=self.conf) self.declare_imports(context) context.save_context(*args, **kwargs) def declare_imports(self, context=None): if not context: ctx_key = self.conf[IMPORTS_CONTEXT_KEY] context = Context(ident=ctx_key, conf=self.conf) self._declare_imports(context) return context def _declare_imports(self, context): 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): context(self.rdf_object).imports(ctx.rdf_object) ctx._declare_imports(context)
[docs] def save_context(self, graph=None, inline_imports=False, autocommit=True, saved_contexts=None): ''' Save the context to a graph ''' if saved_contexts is None: saved_contexts = set([]) if (self._change_counter, id(self)) in saved_contexts: return saved_contexts.add((self._change_counter, id(self))) if graph is None: graph = self._retrieve_configured_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, saved_contexts) 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._save_context_triples()) else: ctx_graph = self.get_target_graph(graph) ctx_graph.addN((s, p, o, ctx_graph) for s, p, o in self._save_context_triples()) if autocommit and hasattr(graph, 'commit'): graph.commit()
save = save_context ''' Alias to save_context ''' @property def triples_saved(self): return self._triples_saved_helper() def _triples_saved_helper(self, seen=None): if seen is None: seen = set() if id(self) in seen: return 0 seen.add(id(self)) res = self._triples_saved for ctx in self._imported_contexts: res += ctx._triples_saved_helper(seen) return res def _save_context_triples(self): self._triples_saved = 0 for x in self._statements: t = x.to_triple() if not (isinstance(t[0], Variable) or isinstance(t[2], Variable) or isinstance(t[1], Variable)): self._triples_saved += 1 yield t 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: yield x.to_triple() def contextualize(self, context): return ContextualizingProxy(context, self) @property def rdf_object(self): if self._rdf_object is None: from PyOpenWorm.contextDataObject import ContextDataObject self._rdf_object = ContextDataObject.contextualize(self.context)(ident=self.identifier) return self._rdf_object.contextualize(self.context) def __bool__(self): return True __nonzero__ = __bool__ def __len__(self): return len(self._statements) 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): ident = getattr(self, 'identifier', '???') if ident is None: identpart = '' else: identpart = 'ident="{}"'.format(ident) return '{}({})'.format(FCN(type(self)), identpart) 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_mixed_graph(self): return ConjunctiveGraph(store=ContextStore(context=self, include_stored=True)) def load_staged_graph(self): return ConjunctiveGraph(store=ContextStore(context=self)) @property def mixed(self): return QueryContext( mapper=self.mapper, graph=self.load_mixed_graph(), ident=self.identifier, conf=self.conf) @property def staged(self): return QueryContext( mapper=self.mapper, graph=self.load_staged_graph(), ident=self.identifier, conf=self.conf) @property def stored(self): return QueryContext( mapper=self.mapper, graph=self.load_graph_from_configured_store(), ident=self.identifier, conf=self.conf) def _retrieve_configured_graph(self): return self.rdf def resolve_class(self, uri): # look up the class in the registryCache if self.mapper is None: return None c = self.mapper.RDFTypeTable.get(uri) if c is None: # otherwise, attempt to load into the cache by # reading the RDF graph. from PyOpenWorm.dataObject import PythonClassDescription, RegistryEntry re = self(RegistryEntry)() re.rdf_class(uri) cd = self(PythonClassDescription)() re.class_description(cd) for cd_l in cd.load(): class_name = cd_l.name() moddo = cd_l.module() mod = self.mapper.load_module(moddo.name()) c = getattr(mod, class_name) break return c
[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
ClassContexts = dict()
[docs]class ClassContextMeta(ContextMeta): def __call__(self, ident): res = ClassContexts.get(ident) if not res: res = super(ClassContextMeta, self).__call__(ident=ident) return res
[docs]class ClassContext(six.with_metaclass(ClassContextMeta, Context)): pass
[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