Mercurial > repos > shellac > guppy_basecaller
diff env/lib/python3.7/site-packages/rdflib/graph.py @ 5:9b1c78e6ba9c draft default tip
"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author | shellac |
---|---|
date | Mon, 01 Jun 2020 08:59:25 -0400 |
parents | 79f47841a781 |
children |
line wrap: on
line diff
--- a/env/lib/python3.7/site-packages/rdflib/graph.py Thu May 14 16:47:39 2020 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1954 +0,0 @@ -from rdflib.term import Literal # required for doctests -assert Literal # avoid warning -from rdflib.namespace import Namespace # required for doctests -assert Namespace # avoid warning -from rdflib.py3compat import format_doctest_out - -__doc__ = format_doctest_out("""\ - -RDFLib defines the following kinds of Graphs: - -* :class:`~rdflib.graph.Graph` -* :class:`~rdflib.graph.QuotedGraph` -* :class:`~rdflib.graph.ConjunctiveGraph` -* :class:`~rdflib.graph.Dataset` - -Graph ------ - -An RDF graph is a set of RDF triples. Graphs support the python ``in`` -operator, as well as iteration and some operations like union, -difference and intersection. - -see :class:`~rdflib.graph.Graph` - -Conjunctive Graph ------------------ - -A Conjunctive Graph is the most relevant collection of graphs that are -considered to be the boundary for closed world assumptions. This -boundary is equivalent to that of the store instance (which is itself -uniquely identified and distinct from other instances of -:class:`Store` that signify other Conjunctive Graphs). It is -equivalent to all the named graphs within it and associated with a -``_default_`` graph which is automatically assigned a :class:`BNode` -for an identifier - if one isn't given. - -see :class:`~rdflib.graph.ConjunctiveGraph` - -Quoted graph ------------- - -The notion of an RDF graph [14] is extended to include the concept of -a formula node. A formula node may occur wherever any other kind of -node can appear. Associated with a formula node is an RDF graph that -is completely disjoint from all other graphs; i.e. has no nodes in -common with any other graph. (It may contain the same labels as other -RDF graphs; because this is, by definition, a separate graph, -considerations of tidiness do not apply between the graph at a formula -node and any other graph.) - -This is intended to map the idea of "{ N3-expression }" that is used -by N3 into an RDF graph upon which RDF semantics is defined. - -see :class:`~rdflib.graph.QuotedGraph` - -Dataset -------- - -The RDF 1.1 Dataset, a small extension to the Conjunctive Graph. The -primary term is "graphs in the datasets" and not "contexts with quads" -so there is a separate method to set/retrieve a graph in a dataset and -to operate with dataset graphs. As a consequence of this approach, -dataset graphs cannot be identified with blank nodes, a name is always -required (RDFLib will automatically add a name if one is not provided -at creation time). This implementation includes a convenience method -to directly add a single quad to a dataset graph. - -see :class:`~rdflib.graph.Dataset` - -Working with graphs -=================== - -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 IOMemory store and an identifier - -<http://rdflib.net>: - - >>> g = Graph('IOMemory', URIRef("http://rdflib.net")) - >>> g.identifier - rdflib.term.URIRef(%(u)s'http://rdflib.net') - >>> str(g) # doctest: +NORMALIZE_WHITESPACE - "<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() - >>> statementId = BNode() - >>> print(len(g)) - 0 - >>> g.add((statementId, RDF.type, RDF.Statement)) - >>> g.add((statementId, RDF.subject, - ... URIRef(%(u)s'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 support set-theoretic operators, you can add/subtract graphs, as -well as intersection (with multiplication operator g1*g2) and xor (g1 -^ g2). - -Note that BNode IDs are kept when doing set-theoretic operations, this -may or may not be what you want. Two named graphs within the same -application probably want share BNode IDs, two graphs with data from -different sources probably not. If your BNode IDs are all generated -by RDFLib they are UUIDs and unique. - - >>> g1 = Graph() - >>> g2 = Graph() - >>> u = URIRef(%(u)s'http://example.com/foo') - >>> g1.add([u, RDFS.label, Literal('foo')]) - >>> g1.add([u, RDFS.label, Literal('bar')]) - >>> g2.add([u, RDFS.label, Literal('foo')]) - >>> g2.add([u, RDFS.label, Literal('bing')]) - >>> len(g1 + g2) # adds bing as label - 3 - >>> len(g1 - g2) # removes foo - 1 - >>> len(g1 * g2) # only foo - 1 - >>> g1 += g2 # now g1 contains everything - - -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(%(u)s'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(%(u)s'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(%(u)s'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 a string - - >>> 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(data=src, format='n3') - >>> print(len(g2)) - 4 - -Using Namespace class: - - >>> RDFLib = Namespace('http://rdflib.net/') - >>> RDFLib.ConjunctiveGraph - rdflib.term.URIRef(%(u)s'http://rdflib.net/ConjunctiveGraph') - >>> RDFLib['Graph'] - rdflib.term.URIRef(%(u)s'http://rdflib.net/Graph') - -""") - -import logging -logger = logging.getLogger(__name__) - -# import md5 -import random -import warnings - -from hashlib import md5 - -try: - from io import BytesIO - assert BytesIO -except ImportError: - try: - from io import StringIO as BytesIO - assert BytesIO - except ImportError: - from io import StringIO as BytesIO - assert BytesIO - -from rdflib.namespace import RDF, RDFS, SKOS - -from rdflib import plugin, exceptions, query - -from rdflib.term import Node, URIRef, Genid -from rdflib.term import BNode - -import rdflib.term - -from rdflib.paths import Path - -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.collection import Collection -from rdflib import py3compat -b = py3compat.b - -import os -import shutil -import tempfile -from urllib.parse import urlparse - -__all__ = [ - 'Graph', 'ConjunctiveGraph', 'QuotedGraph', 'Seq', - 'ModificationException', 'Dataset', - '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/ - - """ - - def __init__(self, store='default', identifier=None, - namespace_manager=None): - super(Graph, self).__init__() - self.__identifier = identifier or BNode() - - if not isinstance(self.__identifier, Node): - self.__identifier = URIRef(self.__identifier) - - 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 - self.default_union = False - - def __get_store(self): - return self.__store - store = property(__get_store) # read-only attr - - def __get_identifier(self): - return self.__identifier - identifier = property(__get_identifier) # read-only attr - - 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, - doc="this graph's 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 toPython(self): - return self - - 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, xxx_todo_changeme): - """Add a triple with self as context""" - (s, p, o) = xxx_todo_changeme - assert isinstance(s, Node), \ - "Subject %s must be an rdflib term" % (s,) - assert isinstance(p, Node), \ - "Predicate %s must be an rdflib term" % (p,) - assert isinstance(o, Node), \ - "Object %s must be an rdflib term" % (o,) - 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 - and _assertnode(s,p,o) - ) - - def remove(self, xxx_todo_changeme1): - """Remove a triple from the graph - - If the triple does not provide a context attribute, removes the triple - from all contexts. - """ - (s, p, o) = xxx_todo_changeme1 - self.__store.remove((s, p, o), context=self) - - def triples(self, xxx_todo_changeme2): - """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. - """ - (s, p, o) = xxx_todo_changeme2 - if isinstance(p, Path): - for _s, _o in p.eval(self, s, o): - yield (_s, p, _o) - else: - for (s, p, o), cg in self.__store.triples((s, p, o), context=self): - yield (s, p, o) - - @py3compat.format_doctest_out - def __getitem__(self, item): - """ - A graph can be "sliced" as a shortcut for the triples method - The python slice syntax is (ab)used for specifying triples. - A generator over matches is returned, - the returned tuples include only the parts not given - - >>> import rdflib - >>> g = rdflib.Graph() - >>> g.add((rdflib.URIRef('urn:bob'), rdflib.RDFS.label, rdflib.Literal('Bob'))) - - >>> list(g[rdflib.URIRef('urn:bob')]) # all triples about bob - [(rdflib.term.URIRef(%(u)s'http://www.w3.org/2000/01/rdf-schema#label'), rdflib.term.Literal(%(u)s'Bob'))] - - >>> list(g[:rdflib.RDFS.label]) # all label triples - [(rdflib.term.URIRef(%(u)s'urn:bob'), rdflib.term.Literal(%(u)s'Bob'))] - - >>> list(g[::rdflib.Literal('Bob')]) # all triples with bob as object - [(rdflib.term.URIRef(%(u)s'urn:bob'), rdflib.term.URIRef(%(u)s'http://www.w3.org/2000/01/rdf-schema#label'))] - - Combined with SPARQL paths, more complex queries can be - written concisely: - - Name of all Bobs friends: - - g[bob : FOAF.knows/FOAF.name ] - - Some label for Bob: - - g[bob : DC.title|FOAF.name|RDFS.label] - - All friends and friends of friends of Bob - - g[bob : FOAF.knows * '+'] - - etc. - - .. versionadded:: 4.0 - - """ - - if isinstance(item, slice): - - s,p,o=item.start,item.stop,item.step - if s is None and p is None and o is None: - return self.triples((s,p,o)) - elif s is None and p is None: - return self.subject_predicates(o) - elif s is None and o is None: - return self.subject_objects(p) - elif p is None and o is None: - return self.predicate_objects(s) - elif s is None: - return self.subjects(p,o) - elif p is None: - return self.predicates(s,o) - elif o is None: - return self.objects(s,p) - else: - # all given - return (s,p,o) in self - - elif isinstance(item, (Path,Node)): - - return self.predicate_objects(item) - - else: - raise TypeError("You can only index a graph by a single rdflib term or path, or a slice of rdflib terms.") - - 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. - BNode IDs are not changed.""" - self.addN((s, p, o, self) for s, p, o in other) - return self - - def __isub__(self, other): - """Subtract all triples in Graph other from Graph. - BNode IDs are not changed.""" - for triple in other: - self.remove(triple) - return self - - def __add__(self, other): - """Set-theoretic union - BNode IDs are not changed.""" - retval = Graph() - for (prefix, uri) in set( - list(self.namespaces()) + list(other.namespaces())): - retval.bind(prefix, uri) - for x in self: - retval.add(x) - for y in other: - retval.add(y) - return retval - - def __mul__(self, other): - """Set-theoretic intersection. - BNode IDs are not changed.""" - retval = Graph() - for x in other: - if x in self: - retval.add(x) - return retval - - def __sub__(self, other): - """Set-theoretic difference. - BNode IDs are not changed.""" - retval = Graph() - for x in self: - if not x in other: - retval.add(x) - return retval - - def __xor__(self, other): - """Set-theoretic XOR. - BNode IDs are not changed.""" - return (self - other) + (other - self) - - __or__ = __add__ - __and__ = __mul__ - - # Conv. methods - - def set(self, triple): - """Convenience method to update the value of object - - Remove any existing triples for subject and predicate before adding - (subject, predicate, object). - """ - (subject, predicate, object_) = triple - assert subject is not None, \ - "s can't be None in .set([s,p,o]), as it would remove (*, p, *)" - assert predicate is not None, \ - "p can't be None in .set([s,p,o]), as it would remove (s, *, *)" - 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, xxx_todo_changeme3, context=None): - (subject, predicate, object_) = xxx_todo_changeme3 - 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 = next(values) - except StopIteration: - retval = default - else: - if any is False: - try: - next(values) - 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: - pass - return retval - - def label(self, subject, default=''): - """Query for the RDFS.label of the subject - - Return default if no label exists or any label if multiple exist. - """ - 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=None, - 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. - - >>> from rdflib import ConjunctiveGraph, URIRef, RDFS, Literal - >>> from rdflib.namespace import SKOS - >>> from pprint import pprint - >>> g = ConjunctiveGraph() - >>> u = URIRef(%(u)s'http://example.com/foo') - >>> g.add([u, RDFS.label, Literal('foo')]) - >>> g.add([u, RDFS.label, Literal('bar')]) - >>> pprint(sorted(g.preferredLabel(u))) - [(rdflib.term.URIRef(%(u)s'http://www.w3.org/2000/01/rdf-schema#label'), - rdflib.term.Literal(%(u)s'bar')), - (rdflib.term.URIRef(%(u)s'http://www.w3.org/2000/01/rdf-schema#label'), - rdflib.term.Literal(%(u)s'foo'))] - >>> g.add([u, SKOS.prefLabel, Literal('bla')]) - >>> pprint(g.preferredLabel(u)) - [(rdflib.term.URIRef(%(u)s'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(%(u)s'http://www.w3.org/2004/02/skos/core#prefLabel'), - rdflib.term.Literal(%(u)s'bla')), - (rdflib.term.URIRef(%(u)s'http://www.w3.org/2004/02/skos/core#prefLabel'), - rdflib.term.Literal(%(u)s'blubb', lang='en'))] - >>> g.preferredLabel(u, lang='') #doctest: +NORMALIZE_WHITESPACE - [(rdflib.term.URIRef(%(u)s'http://www.w3.org/2004/02/skos/core#prefLabel'), - rdflib.term.Literal(%(u)s'bla'))] - >>> pprint(g.preferredLabel(u, lang='en')) - [(rdflib.term.URIRef(%(u)s'http://www.w3.org/2004/02/skos/core#prefLabel'), - rdflib.term.Literal(%(u)s'blubb', lang='en'))] - """ - - if default is None: - default = [] - - # setup the language filtering - if lang is not None: - if lang == '': # we only want not language-tagged literals - langfilter = lambda l: l.language is 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 = list(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. - """ - chain = set([list]) - while list: - item = self.value(list, RDF.first) - if item is not None: - yield item - list = self.value(list, RDF.rest) - if list in chain: - raise ValueError("List contains a recursive rdf:rest reference") - chain.add(list) - - def transitiveClosure(self, func, arg, seen=None): - """ - 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)] # doctest: +NORMALIZE_WHITESPACE - [rdflib.term.BNode('baz'), - rdflib.term.BNode('bar'), - rdflib.term.BNode('foo')] - - >>> [rt for rt in g.transitiveClosure( - ... reverseList,RDF.nil)] # doctest: +NORMALIZE_WHITESPACE - 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')] - - """ - if seen is None: - seen = {} - elif arg in seen: - return - seen[arg] = 1 - for rt in func(arg, self): - yield rt - for rt_2 in self.transitiveClosure(func, rt, seen): - 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 even - if namespace was already bound to a different prefix. - - for example: graph.bind('foaf', 'http://xmlns.com/foaf/0.1/') - - """ - 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', 'trig' and 'nquads' 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, "wb") - 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() - >>> with open(file_name, "r") as f: - ... result = g.parse(f, format="application/rdf+xml") - >>> len(g) - 2 - - >>> os.remove(file_name) - - """ - - 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)() - try: - parser.parse(source, self, **args) - finally: - if source.auto_close: - source.close() - 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=None, initBindings=None, - use_store_provided=True, **kwargs): - """ - Query this graph. - - A type of 'prepared queries' can be realised by providing - initial variable bindings with initBindings - - Initial namespaces are used to resolve prefixes used in the query, - if none are given, the namespaces from the graph's namespace manager - are used. - - :returntype: rdflib.query.QueryResult - - """ - - initBindings = initBindings or {} - initNs = initNs or dict(self.namespaces()) - - if hasattr(self.store, "query") and use_store_provided: - try: - return self.store.query( - query_object, initNs, initBindings, - self.default_union - and '__UNION__' - or self.identifier, - **kwargs) - except NotImplementedError: - pass # store has no own implementation - - 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 update(self, update_object, processor='sparql', - initNs=None, initBindings=None, - use_store_provided=True, **kwargs): - """Update this graph with the given update query.""" - initBindings = initBindings or {} - initNs = initNs or dict(self.namespaces()) - - if hasattr(self.store, "update") and use_store_provided: - try: - return self.store.update( - update_object, initNs, initBindings, - self.default_union - and '__UNION__' - or self.identifier, - **kwargs) - except NotImplementedError: - pass # store has no own implementation - - if not isinstance(processor, query.UpdateProcessor): - processor = plugin.get(processor, query.UpdateProcessor)(self) - - return processor.update(update_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): - """ - does a very basic check if these graphs are the same - If no BNodes are involved, this is accurate. - - See rdflib.compare for a correct implementation of isomorphism checks - """ - # 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): - res = set(self.objects()) - res.update(self.subjects()) - return res - - def collection(self, identifier): - """Create a new ``Collection`` instance. - - Parameters: - - - ``identifier``: a URIRef or BNode instance. - - Example:: - - >>> graph = Graph() - >>> uri = URIRef("http://example.org/resource") - >>> collection = graph.collection(uri) - >>> assert isinstance(collection, Collection) - >>> assert collection.uri is uri - >>> assert collection.graph is graph - >>> collection += [ Literal(1), Literal(2) ] - """ - - return Collection(self, identifier) - - - - 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 - - """ - if not isinstance(identifier, Node): - identifier = URIRef(identifier) - return Resource(self, identifier) - - def _process_skolem_tuples(self, target, func): - for t in self.triples((None, None, None)): - target.add(func(t)) - - def skolemize(self, new_graph=None, bnode=None): - def do_skolemize(bnode, t): - (s, p, o) = t - if s == bnode: - s = s.skolemize() - if o == bnode: - o = o.skolemize() - return (s, p, o) - - def do_skolemize2(t): - (s, p, o) = t - if isinstance(s, BNode): - s = s.skolemize() - if isinstance(o, BNode): - o = o.skolemize() - return (s, p, o) - - retval = Graph() if new_graph is None else new_graph - - if bnode is None: - self._process_skolem_tuples(retval, do_skolemize2) - elif isinstance(bnode, BNode): - self._process_skolem_tuples( - retval, lambda t: do_skolemize(bnode, t)) - - return retval - - def de_skolemize(self, new_graph=None, uriref=None): - def do_de_skolemize(uriref, t): - (s, p, o) = t - if s == uriref: - s = s.de_skolemize() - if o == uriref: - o = o.de_skolemize() - return (s, p, o) - - def do_de_skolemize2(t): - (s, p, o) = t - if isinstance(s, Genid): - s = s.de_skolemize() - if isinstance(o, Genid): - o = o.de_skolemize() - return (s, p, o) - - retval = Graph() if new_graph is None else new_graph - - if uriref is None: - self._process_skolem_tuples(retval, do_de_skolemize2) - elif isinstance(uriref, Genid): - self._process_skolem_tuples( - retval, lambda t: do_de_skolemize(uriref, t)) - - return retval - -class ConjunctiveGraph(Graph): - - """ - A ConjunctiveGraph is an (unnamed) aggregation of all the named - graphs in a store. - - It has a ``default`` graph, whose name is associated with the - graph throughout its life. :meth:`__init__` can take an identifier - to use as the name of this default graph or it will assign a - BNode. - - All methods that add triples work against this default graph. - - All queries are carried out against the union of all graphs. - - """ - - def __init__(self, store='default', identifier=None): - super(ConjunctiveGraph, self).__init__(store, identifier=identifier) - assert self.store.context_aware, ("ConjunctiveGraph must be backed by" - " a context aware store.") - self.context_aware = True - self.default_union = True # Conjunctive! - 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 _spoc(self, triple_or_quad, default=False): - """ - helper method for having methods that support - either triples or quads - """ - if triple_or_quad is None: - return (None, None, None, self.default_context if default else None) - if len(triple_or_quad) == 3: - c = self.default_context if default else None - (s, p, o) = triple_or_quad - elif len(triple_or_quad) == 4: - (s, p, o, c) = triple_or_quad - c = self._graph(c) - return s,p,o,c - - - def __contains__(self, triple_or_quad): - """Support for 'triple/quad in graph' syntax""" - s,p,o,c = self._spoc(triple_or_quad) - for t in self.triples((s,p,o), context=c): - return True - return False - - - def add(self, triple_or_quad): - - """ - Add a triple or quad to the store. - - if a triple is given it is added to the default context - """ - - s,p,o,c = self._spoc(triple_or_quad, default=True) - - _assertnode(s,p,o) - - self.store.add((s, p, o), context=c, quoted=False) - - def _graph(self, c): - if c is None: return None - if not isinstance(c, Graph): - return self.get_context(c) - else: - return c - - - def addN(self, quads): - """Add a sequence of triples with context""" - - self.store.addN( - (s, p, o, self._graph(c)) for s, p, o, c in quads if - _assertnode(s, p, o) - ) - - def remove(self, triple_or_quad): - """ - Removes a triple or quads - - if a triple is given it is removed from all contexts - - a quad is removed from the given context only - - """ - s,p,o,c = self._spoc(triple_or_quad) - - self.store.remove((s, p, o), context=c) - - def triples(self, triple_or_quad, context=None): - """ - Iterate over all the triples in the entire conjunctive graph - - For legacy reasons, this can take the context to query either - as a fourth element of the quad, or as the explicit context - keyword parameter. The kw param takes precedence. - """ - - s,p,o,c = self._spoc(triple_or_quad) - context = self._graph(context or c) - - if self.default_union: - if context==self.default_context: - context = None - else: - if context is None: - context = self.default_context - - if isinstance(p, Path): - if context is None: - context = self - - for s, o in p.eval(context, s, o): - yield (s, p, o) - else: - for (s, p, o), cg in self.store.triples((s, p, o), context=context): - yield s, p, o - - def quads(self, triple_or_quad=None): - """Iterate over all the quads in the entire conjunctive graph""" - - s,p,o,c = self._spoc(triple_or_quad) - - for (s, p, o), cg in self.store.triples((s, p, o), context=c): - for ctx in cg: - yield s, p, o, ctx - - def triples_choices(self, xxx_todo_changeme4, context=None): - """Iterate over all the triples in the entire conjunctive graph""" - (s, p, o) = xxx_todo_changeme4 - if context is None: - if not self.default_union: - context=self.default_context - else: - context = self._graph(context) - - for (s1, p1, o1), cg in self.store.triples_choices((s, p, o), - context=context): - 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): - # TODO: One of these should never happen and probably - # should raise an exception rather than smoothing over - # the weirdness - see #225 - 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) - - g_id = publicID and publicID or source.getPublicId() - if not isinstance(g_id, Node): - g_id = URIRef(g_id) - - context = Graph(store=self.store, identifier=g_id) - context.remove((None, None, None)) # hmm ? - context.parse(source, publicID=publicID, format=format, **args) - return context - - def __reduce__(self): - return (ConjunctiveGraph, (self.store, self.identifier)) - - - -DATASET_DEFAULT_GRAPH_ID = URIRef('urn:x-rdflib:default') - -class Dataset(ConjunctiveGraph): - __doc__ = format_doctest_out(""" - RDF 1.1 Dataset. Small extension to the Conjunctive Graph: - - the primary term is graphs in the datasets and not contexts with quads, - so there is a separate method to set/retrieve a graph in a dataset and - operate with graphs - - graphs cannot be identified with blank nodes - - added a method to directly add a single quad - - Examples of usage: - - >>> # Create a new Dataset - >>> ds = Dataset() - >>> # simple triples goes to default graph - >>> ds.add((URIRef('http://example.org/a'), - ... URIRef('http://www.example.org/b'), - ... Literal('foo'))) - >>> - >>> # Create a graph in the dataset, if the graph name has already been - >>> # used, the corresponding graph will be returned - >>> # (ie, the Dataset keeps track of the constituent graphs) - >>> g = ds.graph(URIRef('http://www.example.com/gr')) - >>> - >>> # add triples to the new graph as usual - >>> g.add( - ... (URIRef('http://example.org/x'), - ... URIRef('http://example.org/y'), - ... Literal('bar')) ) - >>> # alternatively: add a quad to the dataset -> goes to the graph - >>> ds.add( - ... (URIRef('http://example.org/x'), - ... URIRef('http://example.org/z'), - ... Literal('foo-bar'),g) ) - >>> - >>> # querying triples return them all regardless of the graph - >>> for t in ds.triples((None,None,None)): # doctest: +SKIP - ... print(t) # doctest: +NORMALIZE_WHITESPACE - (rdflib.term.URIRef(%(u)s'http://example.org/a'), - rdflib.term.URIRef(%(u)s'http://www.example.org/b'), - rdflib.term.Literal(%(u)s'foo')) - (rdflib.term.URIRef(%(u)s'http://example.org/x'), - rdflib.term.URIRef(%(u)s'http://example.org/z'), - rdflib.term.Literal(%(u)s'foo-bar')) - (rdflib.term.URIRef(%(u)s'http://example.org/x'), - rdflib.term.URIRef(%(u)s'http://example.org/y'), - rdflib.term.Literal(%(u)s'bar')) - >>> - >>> # querying quads return quads; the fourth argument can be unrestricted - >>> # or restricted to a graph - >>> for q in ds.quads((None, None, None, None)): # doctest: +SKIP - ... print(q) # doctest: +NORMALIZE_WHITESPACE - (rdflib.term.URIRef(%(u)s'http://example.org/a'), - rdflib.term.URIRef(%(u)s'http://www.example.org/b'), - rdflib.term.Literal(%(u)s'foo'), - None) - (rdflib.term.URIRef(%(u)s'http://example.org/x'), - rdflib.term.URIRef(%(u)s'http://example.org/y'), - rdflib.term.Literal(%(u)s'bar'), - rdflib.term.URIRef(%(u)s'http://www.example.com/gr')) - (rdflib.term.URIRef(%(u)s'http://example.org/x'), - rdflib.term.URIRef(%(u)s'http://example.org/z'), - rdflib.term.Literal(%(u)s'foo-bar'), - rdflib.term.URIRef(%(u)s'http://www.example.com/gr')) - >>> - >>> for q in ds.quads((None,None,None,g)): # doctest: +SKIP - ... print(q) # doctest: +NORMALIZE_WHITESPACE - (rdflib.term.URIRef(%(u)s'http://example.org/x'), - rdflib.term.URIRef(%(u)s'http://example.org/y'), - rdflib.term.Literal(%(u)s'bar'), - rdflib.term.URIRef(%(u)s'http://www.example.com/gr')) - (rdflib.term.URIRef(%(u)s'http://example.org/x'), - rdflib.term.URIRef(%(u)s'http://example.org/z'), - rdflib.term.Literal(%(u)s'foo-bar'), - rdflib.term.URIRef(%(u)s'http://www.example.com/gr')) - >>> # Note that in the call above - - >>> # ds.quads((None,None,None,'http://www.example.com/gr')) - >>> # would have been accepted, too - >>> - >>> # graph names in the dataset can be queried: - >>> for c in ds.graphs(): # doctest: +SKIP - ... print(c) # doctest: - DEFAULT - http://www.example.com/gr - >>> # A graph can be created without specifying a name; a skolemized genid - >>> # is created on the fly - >>> h = ds.graph() - >>> for c in ds.graphs(): # doctest: +SKIP - ... print(c) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS - DEFAULT - http://rdlib.net/.well-known/genid/rdflib/N... - http://www.example.com/gr - >>> # Note that the Dataset.graphs() call returns names of empty graphs, - >>> # too. This can be restricted: - >>> for c in ds.graphs(empty=False): # doctest: +SKIP - ... print(c) # doctest: +NORMALIZE_WHITESPACE - DEFAULT - http://www.example.com/gr - >>> - >>> # a graph can also be removed from a dataset via ds.remove_graph(g) - - .. versionadded:: 4.0 - """) - - def __init__(self, store='default', default_union=False): - super(Dataset, self).__init__(store=store, identifier=None) - - if not self.store.graph_aware: - raise Exception("DataSet must be backed by a graph-aware store!") - self.default_context = Graph(store=self.store, identifier=DATASET_DEFAULT_GRAPH_ID) - - self.default_union = default_union - - - def __str__(self): - pattern = ("[a rdflib:Dataset;rdflib:storage " - "[a rdflib:Store;rdfs:label '%s']]") - return pattern % self.store.__class__.__name__ - - def graph(self, identifier=None): - if identifier is None: - from rdflib.term import rdflib_skolem_genid - self.bind( - "genid", "http://rdflib.net" + rdflib_skolem_genid, - override=False) - identifier = BNode().skolemize() - - g = self._graph(identifier) - - self.store.add_graph(g) - return g - - def parse(self, source=None, publicID=None, format="xml", - location=None, file=None, data=None, **args): - c = ConjunctiveGraph.parse(self, source, publicID, format, location, file, data, **args) - self.graph(c) - return c - - def add_graph(self, g): - """alias of graph for consistency""" - return self.graph(g) - - def remove_graph(self, g): - if not isinstance(g, Graph): - g = self.get_context(g) - - self.store.remove_graph(g) - if g is None or g == self.default_context: - # default graph cannot be removed - # only triples deleted, so add it back in - self.store.add_graph(self.default_context) - - def contexts(self, triple=None): - default = False - for c in super(Dataset, self).contexts(triple): - default |= c.identifier == DATASET_DEFAULT_GRAPH_ID - yield c - if not default: - yield self.graph(DATASET_DEFAULT_GRAPH_ID) - - graphs = contexts - - def quads(self, quad): - for s, p, o, c in super(Dataset, self).quads(quad): - if c.identifier==self.default_context: - yield (s, p, o, None) - else: - yield (s, p, o, c.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, xxx_todo_changeme5): - """Add a triple with self as context""" - (s, p, o) = xxx_todo_changeme5 - assert isinstance(s, Node), \ - "Subject %s must be an rdflib term" % (s,) - assert isinstance(p, Node), \ - "Predicate %s must be an rdflib term" % (p,) - assert isinstance(o, Node), \ - "Object %s must be an rdflib term" % (o,) - - self.store.add((s, p, o), 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 - and _assertnode(s, p, o) - ) - - 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)) - - -# Make sure QuotedGraph is ordered correctly -# wrt to other Terms. -# this must be done here, as the QuotedGraph cannot be -# circularily imported in term.py -rdflib.term._ORDERING[QuotedGraph]=11 - - -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 toPython(self): - return self - - 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 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, xxx_todo_changeme6): - (s, p, o) = xxx_todo_changeme6 - raise ModificationException() - - def addN(self, quads): - raise ModificationException() - - def remove(self, xxx_todo_changeme7): - (s, p, o) = xxx_todo_changeme7 - raise ModificationException() - - def triples(self, xxx_todo_changeme8): - (s, p, o) = xxx_todo_changeme8 - for graph in self.graphs: - if isinstance(p, Path): - for s, o in p.eval(self, s, o): - yield s, p, o - else: - 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, xxx_todo_changeme9): - """Iterate over all the quads in the entire aggregate graph""" - (s, p, o) = xxx_todo_changeme9 - for graph in self.graphs: - for s1, p1, o1 in graph.triples((s, p, o)): - yield (s1, p1, o1, graph) - - def __len__(self): - return sum(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, xxx_todo_changeme10, context=None): - (subject, predicate, object_) = xxx_todo_changeme10 - 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 _assertnode(*terms): - for t in terms: - assert isinstance(t, Node), \ - 'Term %s must be an rdflib term' % (t,) - return True - - -def test(): - import doctest - doctest.testmod() - -if __name__ == '__main__': - test()