From ce02aaf0e2f7b6563edf8b3cddddba38b47f549a Mon Sep 17 00:00:00 2001 From: Gunnar Aastrand Grimnes Date: Sat, 13 Apr 2013 09:50:30 +0200 Subject: [PATCH 1/3] doc update, fixes #260. --- README.md | 9 +++------ docs/assorted_examples.rst | 3 +-- docs/gettingstarted.rst | 7 +++---- docs/graphs_bnodes.rst | 3 +-- docs/intro_to_graphs.rst | 2 +- docs/intro_to_sparql.rst | 14 +++++++++++--- docs/persistence.rst | 36 ++++++++++++++++++++---------------- 7 files changed, 40 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index e7387a304..df1883c1c 100644 --- a/README.md +++ b/README.md @@ -67,11 +67,13 @@ are listed on [PyPi](pypi.python.org/pypi/rdflib/) RDFLib has a plugin-architecture for store-implementation, as well as parsers/serializers, several other projects exist which extend RDFLib features: - * [rdfextras](https://github.com/RDFLib/rdfextras) - for SPARQL Support + * [rdflib-sparql](https://github.com/RDFLib/rdflib-sparql) - for SPARQL Support * [rdflib-json](https://github.com/RDFLib/rdflib-jsonld) - Serializer and parser for [json-ld](http://json-ld.org) * [rdflib-sparqlstore](https://github.com/RDFLib/rdflib-sparqlstore) - a store implementation on top of a SPARQL endpoint accessed over HTTP * [rdflib-mysql](https://github.com/RDFLib/rdflib-mysql) - a store implementation of top of MySQL + * [rdfextras](https://github.com/RDFLib/rdfextras) - additional experimental plugins and tools + Support ------- @@ -84,11 +86,6 @@ Continuous integration status details available from travis.ci: [![Build Status](https://travis-ci.org/RDFLib/rdflib.png?branch=master)](https://travis-ci.org/RDFLib/rdflib) -The RDFExtras project offers several additional stores as well as a -SPARQL engine for use with RDFLib: - -https://github.com/RDFLib/rdfextras/ - The documentation can be built by doing:: $ python setup.py build_sphinx diff --git a/docs/assorted_examples.rst b/docs/assorted_examples.rst index 961f6daf6..d6404bc5a 100644 --- a/docs/assorted_examples.rst +++ b/docs/assorted_examples.rst @@ -43,8 +43,7 @@ fold_sha1.py might be easier to do some operations on. """ - from rdflib.graph import Graph - from rdflib import Namespace + from rdflib import Graph, Namespace FOAF = Namespace("http://xmlns.com/foaf/0.1/") STABLE = Namespace("http://example.com/person/mbox_sha1sum/") diff --git a/docs/gettingstarted.rst b/docs/gettingstarted.rst index afe05cb9a..56e05d6df 100644 --- a/docs/gettingstarted.rst +++ b/docs/gettingstarted.rst @@ -11,7 +11,7 @@ RDFLib is open source and is maintained in a `GitHub `_ repository. RDFLib releases, current and previous are listed on `PyPi `_ -RDFLib may be easy_installed: +The best way to install RDFLib is to use easy_install or pip: .. code-block :: bash @@ -57,9 +57,7 @@ A more extensive example: .. code-block:: python - from rdflib.graph import Graph - from rdflib import Literal, BNode, Namespace - from rdflib import RDF + from rdflib import Graph, Literal, BNode, Namespace, RDF g = Graph() @@ -89,3 +87,4 @@ A more extensive example: for mbox in g.objects(person, FOAF["mbox"]): print(mbox) +Additional examples can be found in the examples folder in the source distribution. diff --git a/docs/graphs_bnodes.rst b/docs/graphs_bnodes.rst index 1f931bcee..61c5a8aaa 100644 --- a/docs/graphs_bnodes.rst +++ b/docs/graphs_bnodes.rst @@ -11,8 +11,7 @@ Clarifying the query more precisely: .. code-block:: pycon - >>> from rdflib.graph import Graph, ConjunctiveGraph - >>> from rdflib import URIRef + >>> from rdflib import Graph, ConjunctiveGraph, URIRef [1] diff --git a/docs/intro_to_graphs.rst b/docs/intro_to_graphs.rst index 870e3a296..c46342761 100644 --- a/docs/intro_to_graphs.rst +++ b/docs/intro_to_graphs.rst @@ -18,7 +18,7 @@ In an interactive python interpreter, try this: .. code-block:: pycon - >>> from rdflib.graph import Graph + >>> from rdflib import Graph >>> g = Graph() >>> g.parse("demo.nt", format="nt") )> diff --git a/docs/intro_to_sparql.rst b/docs/intro_to_sparql.rst index 3745139d2..1032d8914 100644 --- a/docs/intro_to_sparql.rst +++ b/docs/intro_to_sparql.rst @@ -11,7 +11,7 @@ You might parse some files into a new graph (see `Introduction to parsing Date: Mon, 22 Apr 2013 14:47:59 +0300 Subject: [PATCH 2/3] replaced IOMemory store with new implementation based on Python sets --- rdflib/plugins/memory.py | 648 ++++++++++++++++----------------------- 1 file changed, 262 insertions(+), 386 deletions(-) diff --git a/rdflib/plugins/memory.py b/rdflib/plugins/memory.py index cd91433c3..1b8d4610d 100644 --- a/rdflib/plugins/memory.py +++ b/rdflib/plugins/memory.py @@ -167,406 +167,282 @@ def __contexts(self): class IOMemory(Store): """\ - An integer-key-optimized-context-aware-in-memory store. - - Uses nested dictionaries to store triples and context. Each triple - is stored in six such indices as follows cspo[c][s][p][o] = 1 - and cpos[c][p][o][s] = 1 and cosp[c][o][s][p] = 1 as well as - spo[s][p][o] = [c] and pos[p][o][s] = [c] and pos[o][s][p] = [c] - - Context information is used to track the 'source' of the triple - data for merging, unmerging, remerging purposes. context aware - store stores consume more memory size than non context stores. - + An integer-key-optimized context-aware in-memory store. + + Uses three dict indices (for subjects, objects and predicates) holding + sets of triples. Context information is tracked in a separate dict, with + the triple as key and a dict of {context: quoted} items as value. The + context information is used to filter triple query results. + + Memory usage is low due to several optimizations. RDF nodes are not + stored directly in the indices; instead, the indices hold integer keys + and the actual nodes are only stored once in int-to-object and + object-to-int mapping dictionaries. A default context is determined + based on the first triple that is added to the store, and no context + information is actually stored for subsequent other triples with the + same context information. + + Most operations should be quite fast, but a triples() query with two + bound parts requires a set intersection operation, which may be slow in + some cases. When multiple contexts are used in the same store, filtering + based on context has to be done after each query, which may also be + slow. + """ - context_aware = True formula_aware = True - def __init__(self, configuration=None, identifier=None): - super(IOMemory, self).__init__() - - # indexed by [context][subject][predicate][object] = 1 - self.cspo = self.createIndex() - - # indexed by [context][predicate][object][subject] = 1 - self.cpos = self.createIndex() - - # indexed by [context][object][subject][predicate] = 1 - self.cosp = self.createIndex() - - # indexed by [subject][predicate][object] = [context] - self.spo = self.createIndex() - - # indexed by [predicate][object][subject] = [context] - self.pos = self.createIndex() - - # indexed by [object][subject][predicate] = [context] - self.osp = self.createIndex() - - # indexes integer keys to identifiers - self.forward = self.createForward() - - # reverse index of forward - self.reverse = self.createReverse() - - self.identifier = identifier or BNode() - - self.__namespace = self.createPrefixMap() - self.__prefix = self.createPrefixMap() - - def open(self, configuration, create=False): - if not create: - # An IOMemory Store never exists. - return NO_STORE - else: - return VALID_STORE - - def bind(self, prefix, namespace): - self.__prefix[namespace] = prefix - self.__namespace[prefix] = namespace - - def namespace(self, prefix): - return self.__namespace.get(prefix, None) - - def prefix(self, namespace): - return self.__prefix.get(namespace, None) - - def namespaces(self): - for prefix, namespace in self.__namespace.iteritems(): - yield prefix, namespace - - def defaultContext(self): - return self.default_context - - def addContext(self, context): - """ - Add context w/o adding statement. Dan you can remove this if you want. - """ - - if context not in self.reverse: - ci = randid() - while not self.forward.insert(ci, context): - ci = randid() - self.reverse[context] = ci - - def intToIdentifier(self, (si, pi, oi)): - """ Resolve an integer triple into identifers. """ - return (self.forward[si], self.forward[pi], self.forward[oi]) - - def identifierToInt(self, (s, p, o)): - """ Resolve an identifier triple into integers. """ - return (self.reverse[s], self.reverse[p], self.reverse[o]) - - def uniqueSubjects(self, context=None): - if context is None: - index = self.spo - else: - index = self.cspo[context] - for si in index.keys(): - yield self.forward[si] - - def uniquePredicates(self, context=None): - if context is None: - index = self.pos - else: - index = self.cpos[context] - for pi in index.keys(): - yield self.forward[pi] - - def uniqueObjects(self, context=None): - if context is None: - index = self.osp - else: - index = self.cosp[context] - for oi in index.keys(): - yield self.forward[oi] - - def createForward(self): - return {} - - def createReverse(self): - return {} - - def createIndex(self): - return {} - - def createPrefixMap(self): - return {} - - def add(self, triple, context, quoted=False): - """\ - Add a triple to the store. - """ + # The following variable name conventions are used in this class: + # + # subject, predicate, object unencoded triple parts + # triple = (subject, predicate, object) unencoded triple + # context: unencoded context + # + # sid, pid, oid integer-encoded triple parts + # enctriple = (sid, pid, oid) integer-encoded triple + # cid integer-encoded context + + def __init__(self, configuration=None, identifier=None): + super(IOMemory, self).__init__() + self.__namespace = {} + self.__prefix = {} + + # Mappings for encoding RDF nodes using integer keys, to save memory + # in the indexes Note that None is always mapped to itself, to make + # it easy to test for it in either encoded or unencoded form. + self.__int2obj = {None: None} # maps integer keys to objects + self.__obj2int = {None: None} # maps objects to integer keys + + # Indexes for each triple part, and a list of contexts for each triple + self.__subjectIndex = {} # key: sid val: enctriple + self.__predicateIndex = {} # key: pid val: enctriple + self.__objectIndex = {} # key: oid val: enctriple + self.__tripleContexts = {} # key: enctriple val: {cid1: quoted, cid2: quoted ...} + + # all contexts used in store (unencoded) + self.__all_contexts = set() + # default context information for triples + self.__defaultContexts = None + + def bind(self, prefix, namespace): + self.__prefix[namespace] = prefix + self.__namespace[prefix] = namespace + + def namespace(self, prefix): + return self.__namespace.get(prefix, None) + + def prefix(self, namespace): + return self.__prefix.get(namespace, None) + + def namespaces(self): + for prefix, namespace in self.__namespace.iteritems(): + yield prefix, namespace + + def add(self, triple, context, quoted=False): Store.add(self, triple, context, quoted) - for triple, cg in self.triples(triple, context): - #triple is already in the store. - return - subject, predicate, object = triple - - f = self.forward - r = self.reverse - - # assign keys for new identifiers - - if subject not in r: - si = randid() - while si in f: - si = randid() - f[si] = subject - r[subject] = si - else: - si = r[subject] - - if predicate not in r: - pi = randid() - while pi in f: - pi = randid() - f[pi] = predicate - r[predicate] = pi - else: - pi = r[predicate] - - if object not in r: - oi = randid() - while oi in f: - oi = randid() - f[oi] = object - r[object] = oi - else: - oi = r[object] - - if context not in r: - ci = randid() - while ci in f: - ci = randid() - f[ci] = context - r[context] = ci - else: - ci = r[context] - - # add dictionary entries for cspo[c][s][p][o] = 1, - # cpos[c][p][o][s] = 1, and cosp[c][o][s][p] = 1, creating the - # nested {} where they do not yet exits. - self._setNestedIndex(self.cspo, ci, si, pi, oi) - self._setNestedIndex(self.cpos, ci, pi, oi, si) - self._setNestedIndex(self.cosp, ci, oi, si, pi) - - if not quoted: - self._setNestedIndex(self.spo, si, pi, oi, ci) - self._setNestedIndex(self.pos, pi, oi, si, ci) - self._setNestedIndex(self.osp, oi, si, pi, ci) - - def _setNestedIndex(self, index, *keys): - for key in keys[:-1]: - if key not in index: - index[key] = self.createIndex() - index = index[key] - index[keys[-1]] = 1 - - def _removeNestedIndex(self, index, *keys): - """ Remove context from the list of contexts in a nested index. - - Afterwards, recursively remove nested indexes when they became empty. - """ - parents = [] - for key in keys[:-1]: - parents.append(index) - index = index[key] - del index[keys[-1]] - - n = len(parents) - for i in xrange(n): - index = parents[n - 1 - i] - key = keys[n - 1 - i] - if len(index[key]) == 0: - del index[key] - - def remove(self, triple, context=None): - Store.remove(self, triple, context) + if context is not None and context not in self.__all_contexts: + self.__all_contexts.add(context) + + enctriple = self.__encodeTriple(triple) + sid,pid,oid = enctriple + + self.__addTripleContext(enctriple, context, quoted) + + if sid in self.__subjectIndex: + self.__subjectIndex[sid].add(enctriple) + else: + self.__subjectIndex[sid] = set([enctriple]) + + if pid in self.__predicateIndex: + self.__predicateIndex[pid].add(enctriple) + else: + self.__predicateIndex[pid] = set([enctriple]) + + if oid in self.__objectIndex: + self.__objectIndex[oid].add(enctriple) + else: + self.__objectIndex[oid] = set([enctriple]) + + + def remove(self, triplepat, context=None): + for triple,contexts in self.triples(triplepat, context): + enctriple = self.__encodeTriple(triple) + req_cid = self.__obj2id(context) + for cid in self.__getTripleContexts(enctriple): + if context is not None and req_cid != cid: + continue + self.__removeTripleContext(enctriple, cid) + ctxs = self.__getTripleContexts(enctriple, skipQuoted=True) + if None in ctxs and (context is None or len(ctxs)==1): + self.__removeTripleContext(enctriple, None) + if len(self.__getTripleContexts(enctriple)) == 0: + # triple has been removed from all contexts + sid,pid,oid = enctriple + self.__subjectIndex[sid].remove(enctriple) + self.__predicateIndex[pid].remove(enctriple) + self.__objectIndex[oid].remove(enctriple) + + del self.__tripleContexts[enctriple] + + if triplepat == (None, None, None) and context in self.__all_contexts: + # remove the whole context + self.__all_contexts.remove(context) + + def triples(self, triplein, context=None): if context is not None: - if context == self: + if context == self: # hmm...does this really ever happen? context = None - # f = self.forward - r = self.reverse - if context is None: - for triple, cg in self.triples(triple): - subject, predicate, object = triple - si, pi, oi = self.identifierToInt((subject, predicate, object)) - contexts = list(self.contexts(triple)) - for context in contexts: - ci = r[context] - del self.cspo[ci][si][pi][oi] - del self.cpos[ci][pi][oi][si] - del self.cosp[ci][oi][si][pi] - - self._removeNestedIndex(self.spo, si, pi, oi, ci) - self._removeNestedIndex(self.pos, pi, oi, si, ci) - self._removeNestedIndex(self.osp, oi, si, pi, ci) - # grr!! hafta ref-count these before you can collect - # them dumbass! - #del f[si], f[pi], f[oi] - #del r[subject], r[predicate], r[object] - else: - subject, predicate, object = triple - ci = r.get(context, None) - if ci: - for triple, cg in self.triples(triple, context): - si, pi, oi = self.identifierToInt(triple) - del self.cspo[ci][si][pi][oi] - del self.cpos[ci][pi][oi][si] - del self.cosp[ci][oi][si][pi] - - try: - self._removeNestedIndex(self.spo, si, pi, oi, ci) - self._removeNestedIndex(self.pos, pi, oi, si, ci) - self._removeNestedIndex(self.osp, oi, si, pi, ci) - except KeyError: - # the context may be a quoted one in which - # there will not be a triple in spo, pos or - # osp. So ignore any KeyErrors - pass - # TODO delete references to resources in - # self.forward/self.reverse that are not in use anymore... - - if subject is None and predicate is None and object is None: - # remove context - try: - ci = self.reverse[context] - del self.cspo[ci], self.cpos[ci], self.cosp[ci] - except KeyError: - # TODO: no exception when removing non-existant context? - pass - - def triples(self, triple, context=None): - """A generator over all the triples matching """ + cid = self.__obj2id(context) + enctriple = self.__encodeTriple(triplein) + sid, pid, oid = enctriple + + # all triples case (no triple parts given as pattern) + if sid is None and pid is None and oid is None: + return self.__all_triples(cid) + + # optimize "triple in graph" case (all parts given) + if sid is not None and pid is not None and oid is not None: + if sid in self.__subjectIndex and \ + enctriple in self.__subjectIndex[sid] and \ + self.__tripleHasContext(enctriple, cid): + return ((triplein, self.__contexts(enctriple)) for i in [0]) + else: + return self.__emptygen() + + # remaining cases: one or two out of three given + sets = [] + if sid is not None: + if sid in self.__subjectIndex: + sets.append(self.__subjectIndex[sid]) + else: + return self.__emptygen() + if pid is not None: + if pid in self.__predicateIndex: + sets.append(self.__predicateIndex[pid]) + else: + return self.__emptygen() + if oid is not None: + if oid in self.__objectIndex: + sets.append(self.__objectIndex[oid]) + else: + return self.__emptygen() + + # to get the result, do an intersection of the sets + return ((self.__decodeTriple(enctriple), self.__contexts(enctriple)) + for enctriple in sets[0].intersection(*sets[1:]) + if self.__tripleHasContext(enctriple,cid)) - if context is not None: - if context == self: - context = None - - subject, predicate, object = triple - ci = si = pi = oi = Any - - if context is None: - spo = self.spo - pos = self.pos - osp = self.osp + def contexts(self, triple=None): + if triple is None: + return (context for context in self.__all_contexts) + + enctriple = self.__encodeTriple(triple) + sid,pid,oid = enctriple + if sid in self.__subjectIndex and enctriple in self.__subjectIndex[sid]: + return self.__contexts(enctriple) else: - try: - ci = self.reverse[context] # TODO: Really ignore keyerror here - spo = self.cspo[ci] - pos = self.cpos[ci] - osp = self.cosp[ci] - except KeyError: - return - try: - if subject is not Any: - si = self.reverse[subject] - # throws keyerror if subject doesn't exist ;( - if predicate is not Any: - pi = self.reverse[predicate] - if object is not Any: - oi = self.reverse[object] - except KeyError: - return # raise StopIteration - - if si != Any: # subject is given - if si in spo: - subjectDictionary = spo[si] - if pi != Any: # subject+predicate is given - if pi in subjectDictionary: - if oi != Any: # subject+predicate+object is given - if oi in subjectDictionary[pi]: - ss, pp, oo = self.intToIdentifier((si, pi, oi)) - yield (ss, pp, oo), ( - c for c in self.__icontexts((si, pi, oi))) - else: # given object not found - pass - else: # subject+predicate is given, object unbound - for o in subjectDictionary[pi].keys(): - ss, pp, oo = self.intToIdentifier((si, pi, o)) - yield (ss, pp, oo), ( - c for c in self.__icontexts((si, pi, o))) - else: # given predicate not found - pass - else: # subject given, predicate unbound - for p in subjectDictionary.keys(): - if oi != Any: # object is given - if oi in subjectDictionary[p]: - ss, pp, oo = self.intToIdentifier((si, p, oi)) - yield (ss, pp, oo), ( - c for c in self.__icontexts((si, p, oi))) - else: # given object not found - pass - else: # object unbound - for o in subjectDictionary[p].keys(): - ss, pp, oo = self.intToIdentifier((si, p, o)) - yield (ss, pp, oo), ( - c for c in self.__icontexts((si, p, o))) - else: # given subject not found - pass - elif pi != Any: # predicate is given, subject unbound - if pi in pos: - predicateDictionary = pos[pi] - if oi != Any: # predicate+object is given, subject unbound - if oi in predicateDictionary: - for s in predicateDictionary[oi].keys(): - ss, pp, oo = self.intToIdentifier((s, pi, oi)) - yield (ss, pp, oo), ( - c for c in self.__icontexts((s, pi, oi))) - else: # given object not found - pass - else: # predicate is given, object+subject unbound - for o in predicateDictionary.keys(): - for s in predicateDictionary[o].keys(): - ss, pp, oo = self.intToIdentifier((s, pi, o)) - yield (ss, pp, oo), ( - c for c in self.__icontexts((s, pi, o))) - elif oi != Any: # object is given, subject+predicate unbound - if oi in osp: - objectDictionary = osp[oi] - for s in objectDictionary.keys(): - for p in objectDictionary[s].keys(): - ss, pp, oo = self.intToIdentifier((s, p, oi)) - yield (ss, pp, oo), ( - c for c in self.__icontexts((s, p, oi))) - else: # subject+predicate+object unbound - for s in spo.keys(): - subjectDictionary = spo[s] - for p in subjectDictionary.keys(): - for o in subjectDictionary[p].keys(): - ss, pp, oo = self.intToIdentifier((s, p, o)) - yield (ss, pp, oo), ( - c for c in self.__icontexts((s, p, o))) + return self.__emptygen() def __len__(self, context=None): - - if context is not None: - if context == self: - context = None - - # TODO: for eff. implementation - count = 0 - for triple, cg in self.triples((Any, Any, Any), context): - count += 1 - return count - - def contexts(self, triple=None): - if triple: - si, pi, oi = self.identifierToInt(triple) - for ci in self.spo[si][pi][oi]: - yield self.forward[ci] + cid = self.__obj2id(context) + return sum(1 for enctriple,contexts in self.__all_triples(cid)) + + # internal utility methods below + + def __addTripleContext(self, enctriple, context, quoted): + """add the given context to the set of contexts for the triple""" + cid = self.__obj2id(context) + + sid, pid, oid = enctriple + if sid in self.__subjectIndex and enctriple in self.__subjectIndex[sid]: + # we know the triple exists somewhere in the store + if enctriple not in self.__tripleContexts: + # triple exists with default ctx info + # start with a copy of the default ctx info + self.__tripleContexts[enctriple] = self.__defaultContexts.copy() + + self.__tripleContexts[enctriple][cid] = quoted + if not quoted: + self.__tripleContexts[enctriple][None] = quoted else: - for ci in self.cspo.keys(): - yield self.forward[ci] - - def __icontexts(self, triple): - si, pi, oi = triple - for ci in self.spo[si][pi][oi]: - yield self.forward[ci] + # the triple didn't exist before in the store + if quoted: # this context only + self.__tripleContexts[enctriple] = {cid: quoted} + else: # default context as well + self.__tripleContexts[enctriple] = {cid: quoted, None: quoted} + + # if this is the first ever triple in the store, set default ctx info + if self.__defaultContexts is None: + self.__defaultContexts = self.__tripleContexts[enctriple] + + # if the context info is the same as default, no need to store it + if self.__tripleContexts[enctriple] == self.__defaultContexts: + del self.__tripleContexts[enctriple] + + def __getTripleContexts(self, enctriple, skipQuoted=False): + """return a list of (encoded) contexts for the triple, skipping + quoted contexts if skipQuoted==True""" + + ctxs = self.__tripleContexts.get(enctriple, self.__defaultContexts) + + if not skipQuoted: + return ctxs.keys() + + return [cid for cid,quoted in ctxs.iteritems() if not quoted] + + def __tripleHasContext(self, enctriple, cid): + """return True iff the triple exists in the given context""" + ctxs = self.__tripleContexts.get(enctriple, self.__defaultContexts) + return (cid in ctxs) + + def __removeTripleContext(self, enctriple, cid): + """remove the context from the triple""" + ctxs = self.__tripleContexts.get(enctriple, self.__defaultContexts).copy() + del ctxs[cid] + if ctxs == self.__defaultContexts: + del self.__tripleContexts[enctriple] + else: + self.__tripleContexts[enctriple] = ctxs + + def __obj2id(self, obj): + """encode object, storing it in the encoding map if necessary, and + return the integer key""" + if obj not in self.__obj2int: + id = randid() + while id in self.__int2obj: id=randid() + self.__obj2int[obj] = id + self.__int2obj[id] = obj + return id + return self.__obj2int[obj] + + def __encodeTriple(self, triple): + """encode a whole triple, returning the encoded triple""" + return tuple(map(self.__obj2id, triple)) + + def __decodeTriple(self, enctriple): + """decode a whole encoded triple, returning the original triple""" + return tuple(map(self.__int2obj.get, enctriple)) + + def __all_triples(self, cid): + """return a generator which yields all the triples (unencoded) of + the given context""" + for tset in self.__subjectIndex.itervalues(): + for enctriple in tset.copy(): + if self.__tripleHasContext(enctriple, cid): + yield self.__decodeTriple(enctriple), self.__contexts(enctriple) + + + def __contexts(self, enctriple): + """return a generator for all the non-quoted contexts (unencoded) + the encoded triple appears in""" + return (self.__int2obj.get(cid) for cid in self.__getTripleContexts(enctriple, skipQuoted=True) if cid is not None) + + def __emptygen(self): + """return an empty generator""" + if False: + yield import random From 5837b7c9ab4b58949498c7102747e2fbd9a494a9 Mon Sep 17 00:00:00 2001 From: Osma Suominen Date: Mon, 22 Apr 2013 15:24:06 +0300 Subject: [PATCH 3/3] fix for Python 2.5 compatibility: avoid set.intersection() with multiple parameters, which was introduced in Python 2.6 --- rdflib/plugins/memory.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rdflib/plugins/memory.py b/rdflib/plugins/memory.py index 1b8d4610d..a561353ce 100644 --- a/rdflib/plugins/memory.py +++ b/rdflib/plugins/memory.py @@ -329,9 +329,14 @@ def triples(self, triplein, context=None): else: return self.__emptygen() - # to get the result, do an intersection of the sets + # to get the result, do an intersection of the sets (if necessary) + if len(sets) > 1: + enctriples = sets[0].intersection(*sets[1:]) + else: + enctriples = sets[0].copy() + return ((self.__decodeTriple(enctriple), self.__contexts(enctriple)) - for enctriple in sets[0].intersection(*sets[1:]) + for enctriple in enctriples if self.__tripleHasContext(enctriple,cid)) def contexts(self, triple=None):