diff options
Diffstat (limited to 'creactistore/_templates/lib/rdflib/graph.py')
-rw-r--r-- | creactistore/_templates/lib/rdflib/graph.py | 1486 |
1 files changed, 1486 insertions, 0 deletions
diff --git a/creactistore/_templates/lib/rdflib/graph.py b/creactistore/_templates/lib/rdflib/graph.py new file mode 100644 index 0000000..638a9e2 --- /dev/null +++ b/creactistore/_templates/lib/rdflib/graph.py @@ -0,0 +1,1486 @@ +"""Instantiating Graphs with default store (IOMemory) and default identifier +(a BNode): + + >>> g = Graph() + >>> g.store.__class__ + <class 'rdflib.plugins.memory.IOMemory'> + >>> g.identifier.__class__ + <class 'rdflib.term.BNode'> + +Instantiating Graphs with a specific kind of store (IOMemory) and a default +identifier (a BNode): + +Other store kinds: Sleepycat, MySQL, SQLite + + >>> store = plugin.get('IOMemory', Store)() + >>> store.__class__.__name__ + 'IOMemory' + >>> graph = Graph(store) + >>> graph.store.__class__ + <class 'rdflib.plugins.memory.IOMemory'> + +Instantiating Graphs with Sleepycat store and an identifier - +<http://rdflib.net>: + + >>> g = Graph('IOMemory', URIRef("http://rdflib.net")) + >>> g.identifier + rdflib.term.URIRef('http://rdflib.net') + >>> str(g) + "<http://rdflib.net> a rdfg:Graph;rdflib:storage [a rdflib:Store;rdfs:label 'IOMemory']." + +Creating a ConjunctiveGraph - The top level container for all named Graphs +in a 'database': + + >>> g = ConjunctiveGraph() + >>> str(g.default_context) + "[a rdfg:Graph;rdflib:storage [a rdflib:Store;rdfs:label 'IOMemory']]." + +Adding / removing reified triples to Graph and iterating over it directly or +via triple pattern: + + >>> g = Graph('IOMemory') + >>> statementId = BNode() + >>> print(len(g)) + 0 + >>> g.add((statementId, RDF.type, RDF.Statement)) + >>> g.add((statementId, RDF.subject, URIRef('http://rdflib.net/store/ConjunctiveGraph'))) + >>> g.add((statementId, RDF.predicate, RDFS.label)) + >>> g.add((statementId, RDF.object, Literal("Conjunctive Graph"))) + >>> print(len(g)) + 4 + >>> for s, p, o in g: + ... print(type(s)) + ... + <class 'rdflib.term.BNode'> + <class 'rdflib.term.BNode'> + <class 'rdflib.term.BNode'> + <class 'rdflib.term.BNode'> + + >>> for s, p, o in g.triples((None, RDF.object, None)): + ... print(o) + ... + Conjunctive Graph + >>> g.remove((statementId, RDF.type, RDF.Statement)) + >>> print(len(g)) + 3 + +``None`` terms in calls to :meth:`~rdflib.graph.Graph.triples` can be thought of as "open variables". + +Graph Aggregation - ConjunctiveGraphs and ReadOnlyGraphAggregate within +the same store: + + >>> store = plugin.get('IOMemory', Store)() + >>> g1 = Graph(store) + >>> g2 = Graph(store) + >>> g3 = Graph(store) + >>> stmt1 = BNode() + >>> stmt2 = BNode() + >>> stmt3 = BNode() + >>> g1.add((stmt1, RDF.type, RDF.Statement)) + >>> g1.add((stmt1, RDF.subject, URIRef('http://rdflib.net/store/ConjunctiveGraph'))) + >>> g1.add((stmt1, RDF.predicate, RDFS.label)) + >>> g1.add((stmt1, RDF.object, Literal("Conjunctive Graph"))) + >>> g2.add((stmt2, RDF.type, RDF.Statement)) + >>> g2.add((stmt2, RDF.subject, URIRef('http://rdflib.net/store/ConjunctiveGraph'))) + >>> g2.add((stmt2, RDF.predicate, RDF.type)) + >>> g2.add((stmt2, RDF.object, RDFS.Class)) + >>> g3.add((stmt3, RDF.type, RDF.Statement)) + >>> g3.add((stmt3, RDF.subject, URIRef('http://rdflib.net/store/ConjunctiveGraph'))) + >>> g3.add((stmt3, RDF.predicate, RDFS.comment)) + >>> g3.add((stmt3, RDF.object, Literal("The top-level aggregate graph - The sum of all named graphs within a Store"))) + >>> len(list(ConjunctiveGraph(store).subjects(RDF.type, RDF.Statement))) + 3 + >>> len(list(ReadOnlyGraphAggregate([g1,g2]).subjects(RDF.type, RDF.Statement))) + 2 + +ConjunctiveGraphs have a :meth:`~rdflib.graph.ConjunctiveGraph.quads` method which returns quads instead of +triples, where the fourth item is the Graph (or subclass thereof) instance +in which the triple was asserted: + + >>> uniqueGraphNames = set([graph.identifier for s, p, o, graph in ConjunctiveGraph(store).quads((None, RDF.predicate, None))]) + >>> len(uniqueGraphNames) + 3 + >>> unionGraph = ReadOnlyGraphAggregate([g1, g2]) + >>> uniqueGraphNames = set([graph.identifier for s, p, o, graph in unionGraph.quads((None, RDF.predicate, None))]) + >>> len(uniqueGraphNames) + 2 + +Parsing N3 from StringIO + + >>> from StringIO import StringIO + >>> g2 = Graph() + >>> src = ''' + ... @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . + ... @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . + ... [ a rdf:Statement ; + ... rdf:subject <http://rdflib.net/store#ConjunctiveGraph>; + ... rdf:predicate rdfs:label; + ... rdf:object "Conjunctive Graph" ] . + ... ''' + >>> g2 = g2.parse(StringIO(src), format='n3') + >>> print(len(g2)) + 4 + +Using Namespace class: + + >>> RDFLib = Namespace('http://rdflib.net') + >>> RDFLib.ConjunctiveGraph + rdflib.term.URIRef('http://rdflib.netConjunctiveGraph') + >>> RDFLib['Graph'] + rdflib.term.URIRef('http://rdflib.netGraph') + +""" + +from __future__ import generators + +import logging +_logger = logging.getLogger(__name__) + +#import md5 +import random +import warnings + +try: + from hashlib import md5 +except ImportError: + from md5 import md5 + +try: + from io import BytesIO +except ImportError: + try: + from cStringIO import StringIO as BytesIO + except ImportError: + from StringIO import StringIO as BytesIO + +# # Can't use this approach any longer, this function will raise an ImportError +# # because the sparql module has been moved to the RDFExtras package. + +# def describe(terms,bindings,graph): +# """ +# Default DESCRIBE returns all incomming and outgoing statements about the given terms +# """ +# from rdflib.sparql.sparqlOperators import getValue +# g=Graph() +# terms=[getValue(i)(bindings) for i in terms] +# for s,p,o in graph.triples_choices((terms,None,None)): +# g.add((s,p,o)) +# for s,p,o in graph.triples_choices((None,None,terms)): +# g.add((s,p,o)) +# return g + +from rdflib.namespace import RDF, RDFS, SKOS + +from rdflib import plugin, exceptions, query +#, sparql + +from rdflib.term import Node +from rdflib.term import URIRef +from rdflib.term import BNode +from rdflib.term import Literal # required for doctests +from rdflib.namespace import Namespace # required for doctests +from rdflib.store import Store +from rdflib.serializer import Serializer +from rdflib.parser import Parser +from rdflib.parser import create_input_source +from rdflib.namespace import NamespaceManager +from rdflib.resource import Resource +from rdflib import py3compat +b = py3compat.b + +import tempfile, shutil, os +from urlparse import urlparse + +__all__ = ['Graph', 'ConjunctiveGraph', 'QuotedGraph', 'GraphValue', 'Seq', 'BackwardCompatGraph', 'ModificationException', 'UnSupportedAggregateOperation', 'ReadOnlyGraphAggregate'] + +class Graph(Node): + """An RDF Graph + + The constructor accepts one argument, the 'store' + that will be used to store the graph data (see the 'store' + package for stores currently shipped with rdflib). + + Stores can be context-aware or unaware. Unaware stores take up + (some) less space but cannot support features that require + context, such as true merging/demerging of sub-graphs and + provenance. + + The Graph constructor can take an identifier which identifies the Graph + by name. If none is given, the graph is assigned a BNode for its + identifier. + For more on named graphs, see: http://www.w3.org/2004/03/trix/ + + Ontology for __str__ provenance terms:: + + @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . + @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . + @prefix : <http://rdflib.net/store#> . + @prefix rdfg: <http://www.w3.org/2004/03/trix/rdfg-1/>. + @prefix owl: <http://www.w3.org/2002/07/owl#>. + @prefix log: <http://www.w3.org/2000/10/swap/log#>. + @prefix xsd: <http://www.w3.org/2001/XMLSchema#>. + + :Store a owl:Class; + rdfs:subClassOf <http://xmlns.com/wordnet/1.6/Electronic_database>; + rdfs:subClassOf + [a owl:Restriction; + owl:onProperty rdfs:label; + owl:allValuesFrom [a owl:DataRange; + owl:oneOf ("IOMemory" + "Sleepcat" + "MySQL" + "Redland" + "REGEXMatching" + "ZODB" + "AuditableStorage" + "Memory")] + ]. + + :ConjunctiveGraph a owl:Class; + rdfs:subClassOf rdfg:Graph; + rdfs:label "The top-level graph within the store - the union of all the Graphs within." + rdfs:seeAlso <http://rdflib.net/rdf_store/#ConjunctiveGraph>. + + :DefaultGraph a owl:Class; + rdfs:subClassOf rdfg:Graph; + rdfs:label "The 'default' subgraph of a conjunctive graph". + + + :identifier a owl:Datatypeproperty; + rdfs:label "The store-associated identifier of the formula. ". + rdfs:domain log:Formula + rdfs:range xsd:anyURI; + + :storage a owl:ObjectProperty; + rdfs:domain [ + a owl:Class; + owl:unionOf (log:Formula rdfg:Graph :ConjunctiveGraph) + ]; + rdfs:range :Store. + + :default_context a owl:FunctionalProperty; + rdfs:label "The default context for a conjunctive graph"; + rdfs:domain :ConjunctiveGraph; + rdfs:range :DefaultGraph. + + + {?cg a :ConjunctiveGraph;:storage ?store} + => {?cg owl:sameAs ?store}. + + {?subGraph rdfg:subGraphOf ?cg;a :DefaultGraph} + => {?cg a :ConjunctiveGraph;:default_context ?subGraphOf} . + + """ + + def __init__(self, store='default', identifier=None, + namespace_manager=None): + super(Graph, self).__init__() + self.__identifier = identifier or BNode() + if not isinstance(store, Store): + # TODO: error handling + self.__store = store = plugin.get(store, Store)() + else: + self.__store = store + self.__namespace_manager = namespace_manager + self.context_aware = False + self.formula_aware = False + + def __get_store(self): + return self.__store + store = property(__get_store) + + def __get_identifier(self): + return self.__identifier + identifier = property(__get_identifier) + + def _get_namespace_manager(self): + if self.__namespace_manager is None: + self.__namespace_manager = NamespaceManager(self) + return self.__namespace_manager + + def _set_namespace_manager(self, nm): + self.__namespace_manager = nm + namespace_manager = property(_get_namespace_manager, _set_namespace_manager) + + def __repr__(self): + return "<Graph identifier=%s (%s)>" % (self.identifier, type(self)) + + def __str__(self): + if isinstance(self.identifier,URIRef): + return "%s a rdfg:Graph;rdflib:storage [a rdflib:Store;rdfs:label '%s']."%(self.identifier.n3(),self.store.__class__.__name__) + else: + return "[a rdfg:Graph;rdflib:storage [a rdflib:Store;rdfs:label '%s']]."%(self.store.__class__.__name__) + + def destroy(self, configuration): + """Destroy the store identified by `configuration` if supported""" + self.__store.destroy(configuration) + + #Transactional interfaces (optional) + def commit(self): + """Commits active transactions""" + self.__store.commit() + + def rollback(self): + """Rollback active transactions""" + self.__store.rollback() + + def open(self, configuration, create=False): + """Open the graph store + + Might be necessary for stores that require opening a connection to a + database or acquiring some resource. + """ + return self.__store.open(configuration, create) + + def close(self, commit_pending_transaction=False): + """Close the graph store + + Might be necessary for stores that require closing a connection to a + database or releasing some resource. + """ + self.__store.close(commit_pending_transaction=commit_pending_transaction) + + def add(self, (s, p, o)): + """Add a triple with self as context""" + self.__store.add((s, p, o), self, quoted=False) + + def addN(self, quads): + """Add a sequence of triple with context""" + self.__store.addN([(s, p, o, c) for s, p, o, c in quads + if isinstance(c, Graph) + and c.identifier is self.identifier]) + + def remove(self, (s, p, o)): + """Remove a triple from the graph + + If the triple does not provide a context attribute, removes the triple + from all contexts. + """ + self.__store.remove((s, p, o), context=self) + + def triples(self, (s, p, o)): + """Generator over the triple store + + Returns triples that match the given triple pattern. If triple pattern + does not provide a context, all contexts will be searched. + """ + for (s, p, o), cg in self.__store.triples((s, p, o), context=self): + yield (s, p, o) + + def __len__(self): + """Returns the number of triples in the graph + + If context is specified then the number of triples in the context is + returned instead. + """ + return self.__store.__len__(context=self) + + def __iter__(self): + """Iterates over all triples in the store""" + return self.triples((None, None, None)) + + def __contains__(self, triple): + """Support for 'triple in graph' syntax""" + for triple in self.triples(triple): + return True + return False + + def __hash__(self): + return hash(self.identifier) + + def md5_term_hash(self): + d = md5(str(self.identifier)) + d.update("G") + return d.hexdigest() + + def __cmp__(self, other): + if other is None: + return -1 + elif isinstance(other, Graph): + return cmp(self.identifier, other.identifier) + else: + #Note if None is considered equivalent to owl:Nothing + #Then perhaps a graph with length 0 should be considered + #equivalent to None (if compared to it)? + return 1 + + def __eq__(self, other): + return isinstance(other, Graph) and self.identifier == other.identifier + + def __lt__(self, other): + return (other is None) or (isinstance(other, Graph) and \ + self.identifier < other.identifier) + def __le__(self, other): return self < other or self == other + + def __gt__(self, other): + return (isinstance(other, Graph) and self.identifier > other.identifier) \ + or (other is not None) + def __ge__(self, other): return self > other or self == other + + def __iadd__(self, other): + """Add all triples in Graph other to Graph""" + for triple in other: + self.add(triple) + return self + + def __isub__(self, other): + """Subtract all triples in Graph other from Graph""" + for triple in other: + self.remove(triple) + return self + + def __add__(self,other) : + """Set-theoretic union""" + retval = Graph() + for x in self: + retval.add(x) + for y in other: + retval.add(y) + return retval + + def __mul__(self,other) : + """Set-theoretic intersection""" + retval = Graph() + for x in other: + if x in self: + retval.add(x) + return retval + + def __sub__(self,other) : + """Set-theoretic difference""" + retval = Graph() + for x in self: + if not x in other : + retval.add(x) + return retval + + def __xor__(self,other): + """Set-theoretic XOR""" + return (self - other) + (other - self) + + __or__ = __add__ + __and__ = __mul__ + + # Conv. methods + + def set(self, (subject, predicate, object)): + """Convenience method to update the value of object + + Remove any existing triples for subject and predicate before adding + (subject, predicate, object). + """ + self.remove((subject, predicate, None)) + self.add((subject, predicate, object)) + + def subjects(self, predicate=None, object=None): + """A generator of subjects with the given predicate and object""" + for s, p, o in self.triples((None, predicate, object)): + yield s + + def predicates(self, subject=None, object=None): + """A generator of predicates with the given subject and object""" + for s, p, o in self.triples((subject, None, object)): + yield p + + def objects(self, subject=None, predicate=None): + """A generator of objects with the given subject and predicate""" + for s, p, o in self.triples((subject, predicate, None)): + yield o + + def subject_predicates(self, object=None): + """A generator of (subject, predicate) tuples for the given object""" + for s, p, o in self.triples((None, None, object)): + yield s, p + + def subject_objects(self, predicate=None): + """A generator of (subject, object) tuples for the given predicate""" + for s, p, o in self.triples((None, predicate, None)): + yield s, o + + def predicate_objects(self, subject=None): + """A generator of (predicate, object) tuples for the given subject""" + for s, p, o in self.triples((subject, None, None)): + yield p, o + + def triples_choices(self, (subject, predicate, object_),context=None): + for (s, p, o), cg in self.store.triples_choices( + (subject, predicate, object_), context=self): + yield (s, p, o) + + def value(self, subject=None, predicate=RDF.value, object=None, + default=None, any=True): + """Get a value for a pair of two criteria + + Exactly one of subject, predicate, object must be None. Useful if one + knows that there may only be one value. + + It is one of those situations that occur a lot, hence this + 'macro' like utility + + Parameters: + subject, predicate, object -- exactly one must be None + default -- value to be returned if no values found + any -- if True, return any value in the case there is more than one, + else, raise UniquenessError + """ + retval = default + + if (subject is None and predicate is None) or \ + (subject is None and object is None) or \ + (predicate is None and object is None): + return None + + if object is None: + values = self.objects(subject, predicate) + if subject is None: + values = self.subjects(predicate, object) + if predicate is None: + values = self.predicates(subject, object) + + try: + retval = values.next() + except StopIteration, e: + retval = default + else: + if any is False: + try: + values.next() + msg = ("While trying to find a value for (%s, %s, %s) the" + " following multiple values where found:\n" % + (subject, predicate, object)) + triples = self.store.triples( + (subject, predicate, object), None) + for (s, p, o), contexts in triples: + msg += "(%s, %s, %s)\n (contexts: %s)\n" % ( + s, p, o, list(contexts)) + raise exceptions.UniquenessError(msg) + except StopIteration, e: + pass + return retval + + def label(self, subject, default=''): + """Query for the RDFS.label of the subject + + Return default if no label exists + """ + if subject is None: + return default + return self.value(subject, RDFS.label, default=default, any=True) + + @py3compat.format_doctest_out + def preferredLabel(self, subject, lang=None, default=[], + labelProperties=(SKOS.prefLabel, RDFS.label)): + """ Find the preferred label for subject. + + By default prefers skos:prefLabels over rdfs:labels. In case at least + one prefLabel is found returns those, else returns labels. In case a + language string (e.g., 'en', 'de' or even '' for no lang-tagged + literals) is given, only such labels will be considered. + + Return a list of (labelProp, label) pairs, where labelProp is either + skos:prefLabel or rdfs:label. + + >>> g = ConjunctiveGraph() + >>> u = URIRef('http://example.com/foo') + >>> g.add([u, RDFS.label, Literal('foo')]) + >>> g.add([u, RDFS.label, Literal('bar')]) + >>> sorted(g.preferredLabel(u)) #doctest: +NORMALIZE_WHITESPACE + [(rdflib.term.URIRef('http://www.w3.org/2000/01/rdf-schema#label'), + rdflib.term.Literal(%(u)s'bar')), + (rdflib.term.URIRef('http://www.w3.org/2000/01/rdf-schema#label'), + rdflib.term.Literal(%(u)s'foo'))] + >>> g.add([u, SKOS.prefLabel, Literal('bla')]) + >>> g.preferredLabel(u) #doctest: +NORMALIZE_WHITESPACE + [(rdflib.term.URIRef('http://www.w3.org/2004/02/skos/core#prefLabel'), + rdflib.term.Literal(%(u)s'bla'))] + >>> g.add([u, SKOS.prefLabel, Literal('blubb', lang='en')]) + >>> sorted(g.preferredLabel(u)) #doctest: +NORMALIZE_WHITESPACE + [(rdflib.term.URIRef('http://www.w3.org/2004/02/skos/core#prefLabel'), + rdflib.term.Literal(%(u)s'blubb', lang='en')), + (rdflib.term.URIRef('http://www.w3.org/2004/02/skos/core#prefLabel'), + rdflib.term.Literal(%(u)s'bla'))] + >>> g.preferredLabel(u, lang='') #doctest: +NORMALIZE_WHITESPACE + [(rdflib.term.URIRef('http://www.w3.org/2004/02/skos/core#prefLabel'), + rdflib.term.Literal(%(u)s'bla'))] + >>> g.preferredLabel(u, lang='en') #doctest: +NORMALIZE_WHITESPACE + [(rdflib.term.URIRef('http://www.w3.org/2004/02/skos/core#prefLabel'), + rdflib.term.Literal(%(u)s'blubb', lang='en'))] + """ + + # setup the language filtering + if lang != None: + if lang == '': # we only want not language-tagged literals + langfilter = lambda l: l.language == None + else: + langfilter = lambda l: l.language == lang + else: # we don't care about language tags + langfilter = lambda l: True + + for labelProp in labelProperties: + labels = filter(langfilter, self.objects(subject, labelProp)) + if len(labels) == 0: + continue + else: + return [(labelProp, l) for l in labels] + return default + + def comment(self, subject, default=''): + """Query for the RDFS.comment of the subject + + Return default if no comment exists + """ + if subject is None: + return default + return self.value(subject, RDFS.comment, default=default, any=True) + + def items(self, list): + """Generator over all items in the resource specified by list + + list is an RDF collection. + """ + while list: + item = self.value(list, RDF.first) + if item: + yield item + list = self.value(list, RDF.rest) + + def transitiveClosure(self,func,arg): + """ + Generates transitive closure of a user-defined + function against the graph + + >>> from rdflib.collection import Collection + >>> g=Graph() + >>> a=BNode('foo') + >>> b=BNode('bar') + >>> c=BNode('baz') + >>> g.add((a,RDF.first,RDF.type)) + >>> g.add((a,RDF.rest,b)) + >>> g.add((b,RDF.first,RDFS.label)) + >>> g.add((b,RDF.rest,c)) + >>> g.add((c,RDF.first,RDFS.comment)) + >>> g.add((c,RDF.rest,RDF.nil)) + >>> def topList(node,g): + ... for s in g.subjects(RDF.rest,node): + ... yield s + >>> def reverseList(node,g): + ... for f in g.objects(node,RDF.first): + ... print(f) + ... for s in g.subjects(RDF.rest,node): + ... yield s + + >>> [rt for rt in g.transitiveClosure(topList,RDF.nil)] + [rdflib.term.BNode('baz'), rdflib.term.BNode('bar'), rdflib.term.BNode('foo')] + + >>> [rt for rt in g.transitiveClosure(reverseList,RDF.nil)] + http://www.w3.org/2000/01/rdf-schema#comment + http://www.w3.org/2000/01/rdf-schema#label + http://www.w3.org/1999/02/22-rdf-syntax-ns#type + [rdflib.term.BNode('baz'), rdflib.term.BNode('bar'), rdflib.term.BNode('foo')] + + """ + for rt in func(arg,self): + yield rt + for rt_2 in self.transitiveClosure(func,rt): + yield rt_2 + + def transitive_objects(self, subject, property, remember=None): + """Transitively generate objects for the ``property`` relationship + + Generated objects belong to the depth first transitive closure of the + ``property`` relationship starting at ``subject``. + """ + if remember is None: + remember = {} + if subject in remember: + return + remember[subject] = 1 + yield subject + for object in self.objects(subject, property): + for o in self.transitive_objects(object, property, remember): + yield o + + def transitive_subjects(self, predicate, object, remember=None): + """Transitively generate objects for the ``property`` relationship + + Generated objects belong to the depth first transitive closure of the + ``property`` relationship starting at ``subject``. + """ + if remember is None: + remember = {} + if object in remember: + return + remember[object] = 1 + yield object + for subject in self.subjects(predicate, object): + for s in self.transitive_subjects(predicate, subject, remember): + yield s + + def seq(self, subject): + """Check if subject is an rdf:Seq + + If yes, it returns a Seq class instance, None otherwise. + """ + if (subject, RDF.type, RDF.Seq) in self: + return Seq(self, subject) + else: + return None + + def qname(self, uri): + return self.namespace_manager.qname(uri) + + def compute_qname(self, uri, generate=True): + return self.namespace_manager.compute_qname(uri, generate) + + def bind(self, prefix, namespace, override=True): + """Bind prefix to namespace + + If override is True will bind namespace to given prefix if namespace + was already bound to a different prefix. + """ + return self.namespace_manager.bind( + prefix, namespace, override=override) + + def namespaces(self): + """Generator over all the prefix, namespace tuples""" + for prefix, namespace in self.namespace_manager.namespaces(): + yield prefix, namespace + + def absolutize(self, uri, defrag=1): + """Turn uri into an absolute URI if it's not one already""" + return self.namespace_manager.absolutize(uri, defrag) + + def serialize( + self, destination=None, format="xml", + base=None, encoding=None, **args): + """Serialize the Graph to destination + + If destination is None serialize method returns the serialization as a + string. Format defaults to xml (AKA rdf/xml). + + Format support can be extended with plugins, + but 'xml', 'n3', 'turtle', 'nt', 'pretty-xml', trix' are built in. + """ + serializer = plugin.get(format, Serializer)(self) + if destination is None: + stream = BytesIO() + serializer.serialize(stream, base=base, encoding=encoding, **args) + return stream.getvalue() + if hasattr(destination, "write"): + stream = destination + serializer.serialize(stream, base=base, encoding=encoding, **args) + else: + location = destination + scheme, netloc, path, params, query, fragment = urlparse(location) + if netloc!="": + print("WARNING: not saving as location" + \ + "is not a local file reference") + return + fd, name = tempfile.mkstemp() + stream = os.fdopen(fd) + serializer.serialize(stream, base=base, encoding=encoding, **args) + stream.close() + if hasattr(shutil,"move"): + shutil.move(name, path) + else: + shutil.copy(name, path) + os.remove(name) + + def parse(self, source=None, publicID=None, format=None, + location=None, file=None, data=None, **args): + """ + Parse source adding the resulting triples to the Graph. + + The source is specified using one of source, location, file or + data. + + :Parameters: + + - `source`: An InputSource, file-like object, or string. In the case + of a string the string is the location of the source. + - `location`: A string indicating the relative or absolute URL of the + source. Graph's absolutize method is used if a relative location + is specified. + - `file`: A file-like object. + - `data`: A string containing the data to be parsed. + - `format`: Used if format can not be determined from source. + Defaults to rdf/xml. Format support can be extended with plugins, + but 'xml', 'n3', 'nt', 'trix', 'rdfa' are built in. + - `publicID`: the logical URI to use as the document base. If None + specified the document location is used (at least in the case where + there is a document location). + + :Returns: + + - self, the graph instance. + + Examples: + + >>> my_data = ''' + ... <rdf:RDF + ... xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' + ... xmlns:rdfs='http://www.w3.org/2000/01/rdf-schema#' + ... > + ... <rdf:Description> + ... <rdfs:label>Example</rdfs:label> + ... <rdfs:comment>This is really just an example.</rdfs:comment> + ... </rdf:Description> + ... </rdf:RDF> + ... ''' + >>> import tempfile + >>> fd, file_name = tempfile.mkstemp() + >>> f = os.fdopen(fd, 'w') + >>> dummy = f.write(my_data) # Returns num bytes written on py3 + >>> f.close() + + >>> g = Graph() + >>> result = g.parse(data=my_data, format="application/rdf+xml") + >>> len(g) + 2 + + >>> g = Graph() + >>> result = g.parse(location=file_name, format="application/rdf+xml") + >>> len(g) + 2 + + >>> g = Graph() + >>> result = g.parse(file=open(file_name, "r"), format="application/rdf+xml") + >>> len(g) + 2 + + >>> os.remove(file_name) + + """ + + if format=="xml": + # warn... backward compat. + format = "application/rdf+xml" + source = create_input_source(source=source, publicID=publicID, + location=location, file=file, + data=data, format=format) + if format is None: + format = source.content_type + if format is None: + #raise Exception("Could not determine format for %r. You can" + \ + # "expicitly specify one with the format argument." % source) + format = "application/rdf+xml" + parser = plugin.get(format, Parser)() + parser.parse(source, self, **args) + return self + + def load(self, source, publicID=None, format="xml"): + self.parse(source, publicID, format) + + def query(self, query_object, processor='sparql', result='sparql', initNs={}, initBindings={}, use_store_provided=True, **kwargs): + """ + """ + + + + if hasattr(self.store, "query") and use_store_provided: + return self.store.query(self,query_object, initNs, initBindings, **kwargs) + + if not isinstance(result, query.Result): + result = plugin.get(result, query.Result) + if not isinstance(processor, query.Processor): + processor = plugin.get(processor, query.Processor)(self) + + return result(processor.query(query_object, initBindings, initNs, **kwargs)) + + + def n3(self): + """return an n3 identifier for the Graph""" + return "[%s]" % self.identifier.n3() + + def __reduce__(self): + return (Graph, (self.store, self.identifier,)) + + def isomorphic(self, other): + # TODO: this is only an approximation. + if len(self) != len(other): + return False + for s, p, o in self: + if not isinstance(s, BNode) and not isinstance(o, BNode): + if not (s, p, o) in other: + return False + for s, p, o in other: + if not isinstance(s, BNode) and not isinstance(o, BNode): + if not (s, p, o) in self: + return False + # TODO: very well could be a false positive at this point yet. + return True + + def connected(self): + """Check if the Graph is connected + + The Graph is considered undirectional. + + Performs a search on the Graph, starting from a random node. Then + iteratively goes depth-first through the triplets where the node is + subject and object. Return True if all nodes have been visited and + False if it cannot continue and there are still unvisited nodes left. + """ + all_nodes = list(self.all_nodes()) + discovered = [] + + # take a random one, could also always take the first one, doesn't + # really matter. + if not all_nodes: + return False + + visiting = [all_nodes[random.randrange(len(all_nodes))]] + while visiting: + x = visiting.pop() + if x not in discovered: + discovered.append(x) + for new_x in self.objects(subject=x): + if new_x not in discovered and new_x not in visiting: + visiting.append(new_x) + for new_x in self.subjects(object=x): + if new_x not in discovered and new_x not in visiting: + visiting.append(new_x) + + # optimisation by only considering length, since no new objects can + # be introduced anywhere. + if len(all_nodes) == len(discovered): + return True + else: + return False + + def all_nodes(self): + obj = set(self.objects()) + allNodes = obj.union(set(self.subjects())) + return allNodes + + def resource(self, identifier): + """Create a new ``Resource`` instance. + + Parameters: + + - ``identifier``: a URIRef or BNode instance. + + Example:: + + >>> graph = Graph() + >>> uri = URIRef("http://example.org/resource") + >>> resource = graph.resource(uri) + >>> assert isinstance(resource, Resource) + >>> assert resource.identifier is uri + >>> assert resource.graph is graph + + """ + return Resource(self, identifier) + + +class ConjunctiveGraph(Graph): + """ + A ConjunctiveGraph is an (unamed) aggregation of all the named graphs + within the Store. It has a ``default`` graph, whose name is associated + with the ConjunctiveGraph throughout its life. All methods work against + this default graph. Its constructor can take an identifier to use as the + name of this default graph or it will assign a BNode. + + In practice, it is typical to instantiate a ConjunctiveGraph if you want + to add triples to the Store but don't care to mint a URI for the graph. + Any triples in the graph can still be addressed. + """ + + def __init__(self, store='default', identifier=None): + super(ConjunctiveGraph, self).__init__(store) + assert self.store.context_aware, ("ConjunctiveGraph must be backed by" + " a context aware store.") + self.context_aware = True + self.default_context = Graph(store=self.store, + identifier=identifier or BNode()) + + def __str__(self): + pattern = ("[a rdflib:ConjunctiveGraph;rdflib:storage " + "[a rdflib:Store;rdfs:label '%s']]") + return pattern % self.store.__class__.__name__ + + def __contains__(self, triple_or_quad): + """Support for 'triple/quad in graph' syntax""" + context = None + if len(triple_or_quad) == 4: + context = triple_or_quad[3] + for t in self.triples(triple_or_quad[:3], context=context): + return True + return False + + def add(self, (s, p, o)): + """Add the triple to the default context""" + self.store.add((s, p, o), context=self.default_context, quoted=False) + + def addN(self, quads): + """Add a sequence of triples with context""" + self.store.addN([quad for quad in quads if quad not in self]) + + def remove(self, (s, p, o)): + """Removes from all its contexts""" + self.store.remove((s, p, o), context=None) + + def triples(self, (s, p, o), context=None): + """Iterate over all the triples in the entire conjunctive graph""" + for (s, p, o), cg in self.store.triples((s, p, o), context=context): + yield s, p, o + + def quads(self,(s,p,o)): + """Iterate over all the quads in the entire conjunctive graph""" + for (s, p, o), cg in self.store.triples((s, p, o), context=None): + for ctx in cg: + yield s, p, o, ctx + + def triples_choices(self, (s, p, o)): + """Iterate over all the triples in the entire conjunctive graph""" + for (s1, p1, o1), cg in self.store.triples_choices((s, p, o), + context=None): + yield (s1, p1, o1) + + def __len__(self): + """Number of triples in the entire conjunctive graph""" + return self.store.__len__() + + def contexts(self, triple=None): + """Iterate over all contexts in the graph + + If triple is specified, iterate over all contexts the triple is in. + """ + for context in self.store.contexts(triple): + if isinstance(context, Graph): + yield context + else: + yield self.get_context(context) + + def get_context(self, identifier, quoted=False): + """Return a context graph for the given identifier + + identifier must be a URIRef or BNode. + """ + return Graph(store=self.store, identifier=identifier, + namespace_manager=self) + + def remove_context(self, context): + """Removes the given context from the graph""" + self.store.remove((None, None, None), context) + + def context_id(self, uri, context_id=None): + """URI#context""" + uri = uri.split("#", 1)[0] + if context_id is None: + context_id = "#context" + return URIRef(context_id, base=uri) + + def parse(self, source=None, publicID=None, format="xml", + location=None, file=None, data=None, **args): + """ + Parse source adding the resulting triples to its own context + (sub graph of this graph). + + See :meth:`rdflib.graph.Graph.parse` for documentation on arguments. + + :Returns: + + The graph into which the source was parsed. In the case of n3 + it returns the root context. + """ + + source = create_input_source(source=source, publicID=publicID, + location=location, file=file, data=data, format=format) + + #id = self.context_id(self.absolutize(source.getPublicId())) + context = Graph(store=self.store, identifier= + publicID and URIRef(publicID) or source.getPublicId()) + context.remove((None, None, None)) + context.parse(source, publicID=publicID, format=format, + location=location, file=file, data=data, **args) + return context + + def __reduce__(self): + return (ConjunctiveGraph, (self.store, self.identifier)) + + +class QuotedGraph(Graph): + """ + Quoted Graphs are intended to implement Notation 3 formulae. They are + associated with a required identifier that the N3 parser *must* provide + in order to maintain consistent formulae identification for scenarios + such as implication and other such processing. + """ + def __init__(self, store, identifier): + super(QuotedGraph, self).__init__(store, identifier) + + def add(self, triple): + """Add a triple with self as context""" + self.store.add(triple, self, quoted=True) + + def addN(self,quads): + """Add a sequence of triple with context""" + self.store.addN([(s,p,o,c) for s,p,o,c in quads + if isinstance(c, QuotedGraph) + and c.identifier is self.identifier]) + + def n3(self): + """Return an n3 identifier for the Graph""" + return "{%s}" % self.identifier.n3() + + def __str__(self): + identifier = self.identifier.n3() + label = self.store.__class__.__name__ + pattern = ("{this rdflib.identifier %s;rdflib:storage " + "[a rdflib:Store;rdfs:label '%s']}") + return pattern % (identifier, label) + + def __reduce__(self): + return (QuotedGraph, (self.store, self.identifier)) + + +class GraphValue(QuotedGraph): + def __init__(self, store, identifier=None, graph=None): + if graph is not None: + assert identifier is None + np = store.node_pickler + identifier = md5() + s = list(graph.triples((None, None, None))) + s.sort() + for t in s: + identifier.update(b("^").join((np.dumps(i) for i in t))) + identifier = URIRef("data:%s" % identifier.hexdigest()) + super(GraphValue, self).__init__(store, identifier) + for t in graph: + store.add(t, context=self) + else: + super(GraphValue, self).__init__(store, identifier) + + + def add(self, triple): + raise Exception("not mutable") + + def remove(self, triple): + raise Exception("not mutable") + + def __reduce__(self): + return (GraphValue, (self.store, self.identifier,)) + + +class Seq(object): + """Wrapper around an RDF Seq resource + + It implements a container type in Python with the order of the items + returned corresponding to the Seq content. It is based on the natural + ordering of the predicate names _1, _2, _3, etc, which is the + 'implementation' of a sequence in RDF terms. + """ + + def __init__(self, graph, subject): + """Parameters: + + - graph: + the graph containing the Seq + + - subject: + the subject of a Seq. Note that the init does not + check whether this is a Seq, this is done in whoever + creates this instance! + """ + + _list = self._list = list() + LI_INDEX = URIRef(str(RDF) + "_") + for (p, o) in graph.predicate_objects(subject): + if p.startswith(LI_INDEX): #!= RDF.Seq: # + i = int(p.replace(LI_INDEX, '')) + _list.append((i, o)) + + # here is the trick: the predicates are _1, _2, _3, etc. Ie, + # by sorting the keys (by integer) we have what we want! + _list.sort() + + def __iter__(self): + """Generator over the items in the Seq""" + for _, item in self._list: + yield item + + def __len__(self): + """Length of the Seq""" + return len(self._list) + + def __getitem__(self, index): + """Item given by index from the Seq""" + index, item = self._list.__getitem__(index) + return item + + +class BackwardCompatGraph(ConjunctiveGraph): + + def __init__(self, backend='default'): + warnings.warn("Use ConjunctiveGraph instead. " + "( from rdflib.graph import ConjunctiveGraph )", + DeprecationWarning, stacklevel=2) + super(BackwardCompatGraph, self).__init__(store=backend) + + def __get_backend(self): + return self.store + backend = property(__get_backend) + + def open(self, configuration, create=True): + return ConjunctiveGraph.open(self, configuration, create) + + def add(self, (s, p, o), context=None): + """Add to to the given context or to the default context""" + if context is not None: + c = self.get_context(context) + assert c.identifier == context, "%s != %s" % \ + (c.identifier, context) + else: + c = self.default_context + self.store.add((s, p, o), context=c, quoted=False) + + def remove(self, (s, p, o), context=None): + """Remove from the given context or from the default context""" + if context is not None: + context = self.get_context(context) + self.store.remove((s, p, o), context) + + def triples(self, (s, p, o), context=None): + """Iterate over all the triples in the entire graph""" + if context is not None: + c = self.get_context(context) + assert c.identifier == context + else: + c = None + for (s, p, o), cg in self.store.triples((s, p, o), c): + yield (s, p, o) + + def __len__(self, context=None): + """Number of triples in the entire graph""" + if context is not None: + context = self.get_context(context) + return self.store.__len__(context) + + def get_context(self, identifier, quoted=False): + """Return a context graph for the given identifier + + identifier must be a URIRef or BNode. + """ + assert isinstance(identifier, URIRef) or \ + isinstance(identifier, BNode), type(identifier) + if quoted: + assert False + return QuotedGraph(self.store, identifier) + #return QuotedGraph(self.store, Graph(store=self.store, + # identifier=identifier)) + else: + return Graph(store=self.store, identifier=identifier, + namespace_manager=self) + #return Graph(self.store, Graph(store=self.store, + # identifier=identifier)) + + def remove_context(self, context): + """Remove the given context from the graph""" + self.store.remove((None, None, None), self.get_context(context)) + + def contexts(self, triple=None): + """Iterate over all contexts in the graph + + If triple is specified, iterate over all contexts the triple is in. + """ + for context in self.store.contexts(triple): + yield context.identifier + + def subjects(self, predicate=None, object=None, context=None): + """Generate subjects with the given predicate and object""" + for s, p, o in self.triples((None, predicate, object), context): + yield s + + def predicates(self, subject=None, object=None, context=None): + """Generate predicates with the given subject and object""" + for s, p, o in self.triples((subject, None, object), context): + yield p + + def objects(self, subject=None, predicate=None, context=None): + """Generate objects with the given subject and predicate""" + for s, p, o in self.triples((subject, predicate, None), context): + yield o + + def subject_predicates(self, object=None, context=None): + """Generate (subject, predicate) tuples for the given object""" + for s, p, o in self.triples((None, None, object), context): + yield s, p + + def subject_objects(self, predicate=None, context=None): + """Generate (subject, object) tuples for the given predicate""" + for s, p, o in self.triples((None, predicate, None), context): + yield s, o + + def predicate_objects(self, subject=None, context=None): + """Generate (predicate, object) tuples for the given subject""" + for s, p, o in self.triples((subject, None, None), context): + yield p, o + + def __reduce__(self): + return (BackwardCompatGraph, (self.store, self.identifier)) + + def save(self, destination, format="xml", base=None, encoding=None): + warnings.warn("Use serialize method instead. ", + DeprecationWarning, stacklevel=2) + self.serialize(destination=destination, format=format, base=base, + encoding=encoding) + +class ModificationException(Exception): + + def __init__(self): + pass + + def __str__(self): + return ("Modifications and transactional operations not allowed on " + "ReadOnlyGraphAggregate instances") + +class UnSupportedAggregateOperation(Exception): + + def __init__(self): + pass + + def __str__(self): + return ("This operation is not supported by ReadOnlyGraphAggregate " + "instances") + +class ReadOnlyGraphAggregate(ConjunctiveGraph): + """Utility class for treating a set of graphs as a single graph + + Only read operations are supported (hence the name). Essentially a + ConjunctiveGraph over an explicit subset of the entire store. + """ + + def __init__(self, graphs,store='default'): + if store is not None: + super(ReadOnlyGraphAggregate, self).__init__(store) + Graph.__init__(self, store) + self.__namespace_manager = None + + assert isinstance(graphs, list) and graphs\ + and [g for g in graphs if isinstance(g, Graph)],\ + "graphs argument must be a list of Graphs!!" + self.graphs = graphs + + def __repr__(self): + return "<ReadOnlyGraphAggregate: %s graphs>" % len(self.graphs) + + def destroy(self, configuration): + raise ModificationException() + + #Transactional interfaces (optional) + def commit(self): + raise ModificationException() + + def rollback(self): + raise ModificationException() + + def open(self, configuration, create=False): + # TODO: is there a use case for this method? + for graph in self.graphs: + graph.open(self, configuration, create) + + def close(self): + for graph in self.graphs: + graph.close() + + def add(self, (s, p, o)): + raise ModificationException() + + def addN(self, quads): + raise ModificationException() + + def remove(self, (s, p, o)): + raise ModificationException() + + def triples(self, (s, p, o)): + for graph in self.graphs: + for s1, p1, o1 in graph.triples((s, p, o)): + yield (s1, p1, o1) + + def __contains__(self, triple_or_quad): + context = None + if len(triple_or_quad) == 4: + context = triple_or_quad[3] + for graph in self.graphs: + if context is None or graph.identifier == context.identifier: + if triple_or_quad[:3] in graph: + return True + return False + + def quads(self,(s,p,o)): + """Iterate over all the quads in the entire aggregate graph""" + for graph in self.graphs: + for s1, p1, o1 in graph.triples((s, p, o)): + yield (s1, p1, o1, graph) + + def __len__(self): + return reduce(lambda x, y: x + y, [len(g) for g in self.graphs]) + + def __hash__(self): + raise UnSupportedAggregateOperation() + + def __cmp__(self, other): + if other is None: + return -1 + elif isinstance(other, Graph): + return -1 + elif isinstance(other, ReadOnlyGraphAggregate): + return cmp(self.graphs, other.graphs) + else: + return -1 + + def __iadd__(self, other): + raise ModificationException() + + def __isub__(self, other): + raise ModificationException() + + # Conv. methods + + def triples_choices(self, (subject, predicate, object_), context=None): + for graph in self.graphs: + choices = graph.triples_choices((subject, predicate, object_)) + for (s, p, o) in choices: + yield (s, p, o) + + def qname(self, uri): + if hasattr(self,'namespace_manager') and self.namespace_manager: + return self.namespace_manager.qname(uri) + raise UnSupportedAggregateOperation() + + def compute_qname(self, uri, generate=True): + if hasattr(self,'namespace_manager') and self.namespace_manager: + return self.namespace_manager.compute_qname(uri, generate) + raise UnSupportedAggregateOperation() + + def bind(self, prefix, namespace, override=True): + raise UnSupportedAggregateOperation() + + def namespaces(self): + if hasattr(self,'namespace_manager'): + for prefix, namespace in self.namespace_manager.namespaces(): + yield prefix, namespace + else: + for graph in self.graphs: + for prefix, namespace in graph.namespaces(): + yield prefix, namespace + + def absolutize(self, uri, defrag=1): + raise UnSupportedAggregateOperation() + + def parse(self, source, publicID=None, format="xml", **args): + raise ModificationException() + + def n3(self): + raise UnSupportedAggregateOperation() + + def __reduce__(self): + raise UnSupportedAggregateOperation() + + +def test(): + import doctest + doctest.testmod() + +if __name__ == '__main__': + test() |