summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Car <nicholas.car@surroundaustralia.com>2020-04-17 22:19:39 +1000
committerGitHub <noreply@github.com>2020-04-17 22:19:39 +1000
commit39d07c4a5c9395f1322a269982e69b63cbf4db22 (patch)
tree088033846bfcce87f40afe46d8befc72cbe735b0
parent5c4aadf85c3cb59e71d37fb2fba7a2447ce1b5aa (diff)
parent308abf72f7fb6e8b198c8be8d2683806f5f7a6cd (diff)
downloadrdflib-39d07c4a5c9395f1322a269982e69b63cbf4db22.tar.gz
Merge pull request #1005 from RDFLib/issue_1003
Issue 1003
-rw-r--r--docs/_themes/armstrong/static/rtd.css_t5
-rw-r--r--docs/index.rst10
-rw-r--r--rdflib/graph.py25
-rw-r--r--rdflib/plugins/serializers/rdfxml.py16
-rw-r--r--rdflib/plugins/serializers/trig.py8
-rw-r--r--rdflib/plugins/serializers/trix.py7
-rw-r--r--rdflib/plugins/serializers/turtle.py11
-rw-r--r--test/test_issue1003.py117
8 files changed, 176 insertions, 23 deletions
diff --git a/docs/_themes/armstrong/static/rtd.css_t b/docs/_themes/armstrong/static/rtd.css_t
index fe102205..f5f08642 100644
--- a/docs/_themes/armstrong/static/rtd.css_t
+++ b/docs/_themes/armstrong/static/rtd.css_t
@@ -779,8 +779,3 @@ p {
}
}
-
-
-p.logo {
- padding-top: 30px;
-} \ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
index 892e370b..7ac3d389 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -4,8 +4,8 @@
rdflib |release|
================
-RDFLib is a pure Python package for working with `RDF <http://www.w3.org/RDF/>`_. RDFLib contains useful APIs,
-including:
+RDFLib is a pure Python package for working with `RDF <http://www.w3.org/RDF/>`_. RDFLib contains useful APIs for
+working with RDF, including:
* **Parsers & Serializers**
@@ -29,7 +29,7 @@ including:
Getting started
---------------
-If you have never used RDFLib before, the following might help get you started:
+If you have never used RDFLib, the following will help get you started:
.. toctree::
:maxdepth: 1
@@ -45,7 +45,7 @@ If you have never used RDFLib before, the following might help get you started:
In depth
--------
-If you are already familiar with RDF and are looking for details about RDFLib handles RDF, these are for you.
+If you are familiar with RDF and are looking for details on how RDFLib handles RDF, these are for you.
.. toctree::
:maxdepth: 1
@@ -87,7 +87,7 @@ For developers
univrdfstore
persisting_n3_terms
-Developers might also like to join rdflib's 'dev' mailing list: `<https://groups.google.com/group/rdflib-dev>`__
+Developers might also like to join rdflib's dev mailing list: `<https://groups.google.com/group/rdflib-dev>`__
The Code
diff --git a/rdflib/graph.py b/rdflib/graph.py
index efd58eba..80dc02f8 100644
--- a/rdflib/graph.py
+++ b/rdflib/graph.py
@@ -293,8 +293,9 @@ class Graph(Node):
For more on named graphs, see: http://www.w3.org/2004/03/trix/
"""
- def __init__(self, store="default", identifier=None, namespace_manager=None):
+ def __init__(self, store="default", identifier=None, namespace_manager=None, base=None):
super(Graph, self).__init__()
+ self.base = base
self.__identifier = identifier or BNode()
if not isinstance(self.__identifier, Node):
@@ -952,6 +953,11 @@ class Graph(Node):
Format support can be extended with plugins,
but "xml", "n3", "turtle", "nt", "pretty-xml", "trix", "trig" and "nquads" are built in.
"""
+
+ # if base is not given as attribute use the base set for the graph
+ if base is None:
+ base = self.base
+
serializer = plugin.get(format, Serializer)(self)
if destination is None:
stream = BytesIO()
@@ -1336,14 +1342,16 @@ class ConjunctiveGraph(Graph):
All queries are carried out against the union of all graphs.
"""
- def __init__(self, store="default", identifier=None):
+ def __init__(self, store="default", identifier=None, default_graph_base=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())
+ self.default_context = Graph(
+ store=self.store, identifier=identifier or BNode(), base=default_graph_base
+ )
def __str__(self):
pattern = (
@@ -1483,12 +1491,12 @@ class ConjunctiveGraph(Graph):
else:
yield self.get_context(context)
- def get_context(self, identifier, quoted=False):
+ def get_context(self, identifier, quoted=False, base=None):
"""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)
+ return Graph(store=self.store, identifier=identifier, namespace_manager=self, base=base)
def remove_context(self, context):
"""Removes the given context from the graph"""
@@ -1651,13 +1659,13 @@ class Dataset(ConjunctiveGraph):
.. versionadded:: 4.0
"""
- def __init__(self, store="default", default_union=False):
+ def __init__(self, store="default", default_union=False, default_graph_base=None):
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
+ store=self.store, identifier=DATASET_DEFAULT_GRAPH_ID, base=default_graph_base
)
self.default_union = default_union
@@ -1668,7 +1676,7 @@ class Dataset(ConjunctiveGraph):
)
return pattern % self.store.__class__.__name__
- def graph(self, identifier=None):
+ def graph(self, identifier=None, base=None):
if identifier is None:
from rdflib.term import rdflib_skolem_genid
@@ -1678,6 +1686,7 @@ class Dataset(ConjunctiveGraph):
identifier = BNode().skolemize()
g = self._graph(identifier)
+ g.base = base
self.store.add_graph(g)
return g
diff --git a/rdflib/plugins/serializers/rdfxml.py b/rdflib/plugins/serializers/rdfxml.py
index 631c8fe0..4242fc05 100644
--- a/rdflib/plugins/serializers/rdfxml.py
+++ b/rdflib/plugins/serializers/rdfxml.py
@@ -46,7 +46,11 @@ class XMLSerializer(Serializer):
yield prefix, namespace
def serialize(self, stream, base=None, encoding=None, **args):
- self.base = base
+ # if base is given here, use that, if not and a base is set for the graph use that
+ if base is not None:
+ self.base = base
+ elif self.store.base is not None:
+ self.base = self.store.base
self.__stream = stream
self.__serialized = {}
encoding = self.encoding
@@ -62,6 +66,8 @@ class XMLSerializer(Serializer):
# If provided, write xml:base attribute for the RDF
if "xml_base" in args:
write(' xml:base="%s"\n' % args['xml_base'])
+ elif self.base:
+ write(' xml:base="%s"\n' % self.base)
# TODO:
# assert(
# namespaces["http://www.w3.org/1999/02/22-rdf-syntax-ns#"]=='rdf')
@@ -163,7 +169,11 @@ class PrettyXMLSerializer(Serializer):
def serialize(self, stream, base=None, encoding=None, **args):
self.__serialized = {}
store = self.store
- self.base = base
+ # if base is given here, use that, if not and a base is set for the graph use that
+ if base is not None:
+ self.base = base
+ elif store.base is not None:
+ self.base = store.base
self.max_depth = args.get("max_depth", 3)
assert self.max_depth > 0, "max_depth must be greater than 0"
@@ -184,6 +194,8 @@ class PrettyXMLSerializer(Serializer):
if "xml_base" in args:
writer.attribute(XMLBASE, args["xml_base"])
+ elif self.base:
+ writer.attribute(XMLBASE, self.base)
writer.namespaces(namespaces.items())
diff --git a/rdflib/plugins/serializers/trig.py b/rdflib/plugins/serializers/trig.py
index 6c05ad75..250e1c09 100644
--- a/rdflib/plugins/serializers/trig.py
+++ b/rdflib/plugins/serializers/trig.py
@@ -39,7 +39,7 @@ class TrigSerializer(TurtleSerializer):
for triple in context:
self.preprocessTriple(triple)
- self._contexts[context]=(self.orderSubjects(), self._subjects, self._references)
+ self._contexts[context] = (self.orderSubjects(), self._subjects, self._references)
def reset(self):
super(TrigSerializer, self).reset()
@@ -49,7 +49,11 @@ class TrigSerializer(TurtleSerializer):
spacious=None, **args):
self.reset()
self.stream = stream
- self.base = base
+ # if base is given here, use that, if not and a base is set for the graph use that
+ if base is not None:
+ self.base = base
+ elif self.store.base is not None:
+ self.base = self.store.base
if spacious is not None:
self._spacious = spacious
diff --git a/rdflib/plugins/serializers/trix.py b/rdflib/plugins/serializers/trix.py
index fceec6bd..0a574493 100644
--- a/rdflib/plugins/serializers/trix.py
+++ b/rdflib/plugins/serializers/trix.py
@@ -29,6 +29,11 @@ class TriXSerializer(Serializer):
self.writer = XMLWriter(stream, nm, encoding, extra_ns={"": TRIXNS})
self.writer.push(TRIXNS[u"TriX"])
+ # if base is given here, use that, if not and a base is set for the graph use that
+ if base is None and self.store.base is not None:
+ base = self.store.base
+ if base is not None:
+ self.writer.attribute("http://www.w3.org/XML/1998/namespacebase", base)
self.writer.namespaces()
if isinstance(self.store, ConjunctiveGraph):
@@ -44,6 +49,8 @@ class TriXSerializer(Serializer):
def _writeGraph(self, graph):
self.writer.push(TRIXNS[u"graph"])
+ if graph.base:
+ self.writer.attribute("http://www.w3.org/XML/1998/namespacebase", graph.base)
if isinstance(graph.identifier, URIRef):
self.writer.element(
TRIXNS[u"uri"], content=text_type(graph.identifier))
diff --git a/rdflib/plugins/serializers/turtle.py b/rdflib/plugins/serializers/turtle.py
index 1c58ba1b..8a41587c 100644
--- a/rdflib/plugins/serializers/turtle.py
+++ b/rdflib/plugins/serializers/turtle.py
@@ -224,7 +224,11 @@ class TurtleSerializer(RecursiveSerializer):
spacious=None, **args):
self.reset()
self.stream = stream
- self.base = base
+ # if base is given here, use that, if not and a base is set for the graph use that
+ if base is not None:
+ self.base = base
+ elif self.store.base is not None:
+ self.base = self.store.base
if spacious is not None:
self._spacious = spacious
@@ -246,6 +250,8 @@ class TurtleSerializer(RecursiveSerializer):
self.endDocument()
stream.write(b("\n"))
+ self.base = None
+
def preprocessTriple(self, triple):
super(TurtleSerializer, self).preprocessTriple(triple)
for i, node in enumerate(triple):
@@ -291,6 +297,9 @@ class TurtleSerializer(RecursiveSerializer):
def startDocument(self):
self._started = True
ns_list = sorted(self.namespaces.items())
+
+ if self.base:
+ self.write(self.indent() + '@base <%s> .\n' % self.base)
for prefix, uri in ns_list:
self.write(self.indent() + '@prefix %s: <%s> .\n' % (prefix, uri))
if ns_list and self._spacious:
diff --git a/test/test_issue1003.py b/test/test_issue1003.py
new file mode 100644
index 00000000..fdc56c82
--- /dev/null
+++ b/test/test_issue1003.py
@@ -0,0 +1,117 @@
+from rdflib import Graph, Dataset, Literal, Namespace, RDF, URIRef
+from rdflib.namespace import SKOS, DCTERMS
+
+"""
+Testing scenarios:
+ 1. no base set
+ 2. base set at graph creation
+ 3. base set at serialization
+ 4. base set at both graph creation & serialization, serialization overrides
+ 5. multiple serialization side effect checking
+ 6. checking results for RDF/XML
+ 7. checking results for N3
+ 8. checking results for TriX & TriG
+"""
+
+# variables
+base_one = Namespace("http://one.org/")
+base_two = Namespace("http://two.org/")
+title = Literal("Title", lang="en")
+description = Literal("Test Description", lang="en")
+creator = URIRef("https://creator.com")
+cs = URIRef("")
+
+# starting graph
+g = Graph()
+g.add((cs, RDF.type, SKOS.ConceptScheme))
+g.add((cs, DCTERMS.creator, creator))
+g.add((cs, DCTERMS.source, URIRef("nick")))
+g.bind("dct", DCTERMS)
+g.bind("skos", SKOS)
+
+
+# 1. no base set for graph, no base set for serialization
+g1 = Graph()
+g1 += g
+# @base should not be in output
+assert "@base" not in g.serialize(format='turtle').decode("utf-8")
+
+
+# 2. base one set for graph, no base set for serialization
+g2 = Graph(base=base_one)
+g2 += g
+# @base should be in output, from Graph (one)
+assert "@base <http://one.org/> ." in g2.serialize(format='turtle').decode("utf-8")
+
+
+# 3. no base set for graph, base two set for serialization
+g3 = Graph()
+g3 += g
+# @base should be in output, from serialization (two)
+assert "@base <http://two.org/> ." in g3.serialize(format='turtle', base=base_two).decode("utf-8")
+
+
+# 4. base one set for graph, base two set for serialization, Graph one overrides
+g4 = Graph(base=base_one)
+g4 += g
+# @base should be in output, from graph (one)
+assert "@base <http://two.org/> ." in g4.serialize(format='turtle', base=base_two).decode("utf-8")
+# just checking that the serialization setting (two) hasn't snuck through
+assert "@base <http://one.org/> ." not in g4.serialize(format='turtle', base=base_two).decode("utf-8")
+
+
+# 5. multiple serialization side effect checking
+g5 = Graph()
+g5 += g
+# @base should be in output, from serialization (two)
+assert "@base <http://two.org/> ." in g5.serialize(format='turtle', base=base_two).decode("utf-8")
+
+# checking for side affects - no base now set for this serialization
+# @base should not be in output
+assert "@base" not in g5.serialize(format='turtle').decode("utf-8")
+
+
+# 6. checking results for RDF/XML
+g6 = Graph()
+g6 += g
+g6.bind("dct", DCTERMS)
+g6.bind("skos", SKOS)
+assert "@xml:base" not in g6.serialize(format='xml').decode("utf-8")
+assert 'xml:base="http://one.org/"' in g6.serialize(format='xml', base=base_one).decode("utf-8")
+g6.base = base_two
+assert 'xml:base="http://two.org/"' in g6.serialize(format='xml').decode("utf-8")
+assert 'xml:base="http://one.org/"' in g6.serialize(format='xml', base=base_one).decode("utf-8")
+
+# 7. checking results for N3
+g7 = Graph()
+g7 += g
+g7.bind("dct", DCTERMS)
+g7.bind("skos", SKOS)
+assert "@xml:base" not in g7.serialize(format='xml').decode("utf-8")
+assert "@base <http://one.org/> ." in g7.serialize(format='n3', base=base_one).decode("utf-8")
+g7.base = base_two
+assert "@base <http://two.org/> ." in g7.serialize(format='n3').decode("utf-8")
+assert "@base <http://one.org/> ." in g7.serialize(format='n3', base=base_one).decode("utf-8")
+
+# 8. checking results for TriX & TriG
+# TriX can specify a base per graph but setting a base for the whole
+base_three = Namespace("http://three.org/")
+ds1 = Dataset()
+ds1.bind("dct", DCTERMS)
+ds1.bind("skos", SKOS)
+g8 = ds1.graph(URIRef('http://g8.com/'), base=base_one)
+g9 = ds1.graph(URIRef('http://g9.com/'))
+g8 += g
+g9 += g
+g9.base = base_two
+ds1.base = base_three
+
+trix = ds1.serialize(format='trix', base=Namespace("http://two.org/")).decode("utf-8")
+assert '<graph xml:base="http://one.org/">' in trix
+assert '<graph xml:base="http://two.org/">' in trix
+assert '<TriX xml:base="http://two.org/"' in trix
+
+trig = ds1.serialize(format='trig', base=Namespace("http://two.org/")).decode("utf-8")
+assert '@base <http://one.org/> .' not in trig
+assert '@base <http://three.org/> .' not in trig
+assert '@base <http://two.org/> .' in trig