from test.data import context1, context2, tarek import pytest from rdflib import ConjunctiveGraph, Graph, Literal from rdflib.namespace import OWL, Namespace, NamespaceManager from rdflib.plugins.stores.memory import Memory from rdflib.term import URIRef foaf1_uri = URIRef("http://xmlns.com/foaf/0.1/") foaf2_uri = URIRef("http://xmlns.com/foaf/2.0/") FOAF1 = Namespace(foaf1_uri) FOAF2 = Namespace(foaf2_uri) def test_binding_replace(): # The confusion here is in the two arguments “override” and “replace” and # how they serve two different purposes --- changing a prefix already bound # to a namespace versus changing a namespace already bound to a prefix. g = Graph(bind_namespaces="none") assert len(list(g.namespaces())) == 0 # 1. Changing the namespace of an existing namespace=>prefix binding: # Say they release FOAF 2.0 and you want to change the binding of # `http://xmlns.com/foaf/2.0/` to `foaf`. # Up to now you had `http://xmlns.com/foaf/0.1/=>foaf` and `http://xmlns.com/foaf/2.0/=>foaf2` # We'll just set up those two bindings ... g.bind("foaf", FOAF1) g.bind("foaf2", FOAF2) assert len(list(g.namespaces())) == 2 assert list(g.namespaces()) == [("foaf", foaf1_uri), ("foaf2", foaf2_uri)] # Now you want to "upgrade" to FOAF2=>foaf and try the obvious: g.bind("foaf", FOAF2) # But that doesn't happen, instead a different prefix, `foaf1` is bound: assert list(g.namespaces()) == [("foaf", foaf1_uri), ("foaf1", foaf2_uri)] # The rationale behind this behaviour is that the prefix "foaf" was already # bound to the FOAF1 namespace, so a different prefix for the FOAF2 namespace # was automatically created. # You can achieve the intended result by setting `replace` to `True`: g.bind("foaf", FOAF2, replace=True) # Because the FOAF2 namespace was rebound to a different prefix, the old # "foaf2" prefix has gone and because the "foaf" prefix was rebound to a # different namespace, the old FOAF1 namespace has gone, leaving just: assert list(g.namespaces()) == [("foaf", foaf2_uri)] # Now, if you wish, you can re-bind the FOAF1.0 namespace to a prefix # of your choice g.bind("oldfoaf", FOAF1) assert list(g.namespaces()) == [ ("foaf", foaf2_uri), # Should be present but has been removed. ("oldfoaf", foaf1_uri), ] # 2. Changing the prefix of an existing namespace=>prefix binding: # The namespace manager preserves the existing bindings from any # subsequent unwanted programmatic rebindings such as: g.bind("foaf", FOAF1) # Which, as things stand, results in: assert list(g.namespaces()) == [("foaf", foaf2_uri), ("foaf1", foaf1_uri)] # In which the attempted change from `oldfoaf` to (the already # bound-to-a different-namespace `foaf`) was intercepted and # changed to a non-clashing prefix of `foaf1` # So, after undoing the above unwanted rebinding .. g.bind("oldfoaf", FOAF1, replace=True) # The bindings are again as desired assert list(g.namespaces()) == [("foaf", foaf2_uri), ("oldfoaf", foaf1_uri)] # Next time through, set override to False g.bind("foaf", FOAF1, override=False) # And the bindings will remain as desired assert list(g.namespaces()) == [("foaf", foaf2_uri), ("oldfoaf", foaf1_uri)] # 3. Parsing data with prefix=>namespace bindings # Let's see the situation regarding namespace bindings # in parsed data - it can be a bit confusing ... # Starting with a very likely example where `foaf` is a # prefix for `FOAF1` data = """\ @prefix foaf: . foaf:name "Bob" .""" g.parse(data=data, format="n3") # The FOAF2 namespace is already bound to `foaf` so a # non-clashing prefix of `foaf1` is rebound to FOAF1 in # place of the existing `oldfoaf` prefix assert list(g.namespaces()) == [("foaf", foaf2_uri), ("foaf1", foaf1_uri)] # Yes, namespace bindings in parsed data replace existing # bindings (i.e. `oldfoaf` was replaced by `foaf1`), just # live with it ... # A different example of the same principle where `foaf2` # replaces the exsting `foaf` data = """\ @prefix foaf2: . foaf2:name "Alice" .""" g.parse(data=data, format="n3") # The already-bound namespace of `foaf=>FOAF2` is replaced # by the `foaf2=>FOAF2` binding specified in the N3 source assert list(g.namespaces()) == [ ("foaf1", foaf1_uri), ("foaf2", foaf2_uri), ] # Where a prefix-namespace binding in the data does # not clash with the existing binding ... data = """\ @prefix foaf1: . foaf1:name "Bob" .""" g.parse(data=data, format="n3") # The prefix `foaf1`, already bound to FOAF1 is # used assert list(g.namespaces()) == [ ("foaf1", foaf1_uri), ("foaf2", foaf2_uri), ] # Otherwise, the existing prefix binding is replaced data = """\ @prefix foaf: . foaf:name "Alice" .""" g.parse(data=data, format="n3") assert list(g.namespaces()) == [ ("foaf2", foaf2_uri), ("foaf", foaf1_uri), ] # Prefixes are bound to namespaces which in turn have a URIRef # representation - so a different prefix=>namespace binding # means a different predicate and thus a different statement: expected = """ @prefix foaf: . @prefix foaf2: . foaf:name "Bob" . foaf:name "Alice" ; foaf2:name "Alice" . """ s = g.serialize(format="n3") for l in expected.split(): assert l in s def test_prefix_alias_disallowed(): # CANNOT BIND A PREFIX ALIASED TO AN EXISTING BOUND NAMESPACE g = Graph(bind_namespaces="none") g.bind("owl", OWL) assert ("owl", URIRef("http://www.w3.org/2002/07/owl#")) in list(g.namespaces()) g.bind("wol", OWL, override=False) assert ("owl", URIRef("http://www.w3.org/2002/07/owl#")) in list(g.namespaces()) assert ("wol", URIRef("http://www.w3.org/2002/07/owl#")) not in list(g.namespaces()) def test_rebind_prefix_succeeds(): # CAN REPLACE A PREFIX-NAMESPACE BINDING g = Graph(bind_namespaces="none") g.bind("owl", OWL) assert ("owl", URIRef("http://www.w3.org/2002/07/owl#")) in list(g.namespaces()) g.bind("wol", OWL) assert ("wol", URIRef("http://www.w3.org/2002/07/owl#")) in list(g.namespaces()) assert ("owl", URIRef("http://www.w3.org/2002/07/owl#")) not in list(g.namespaces()) def test_parse_rebinds_prefix(): # PARSED PREFIX-NAMESPACE BINDINGS REPLACE EXISTING BINDINGS data = """@prefix friend-of-a-friend: . friend-of-a-friend:name "Bob" . """ # Use full set of namespace bindings, including foaf g = Graph(bind_namespaces="none") g.bind("foaf", FOAF1) assert ("foaf", foaf1_uri) in list(g.namespaces()) g.parse(data=data, format="n3") # foaf no longer in set of namespace bindings assert ("foaf", foaf1_uri) not in list(g.namespaces()) assert ("friend-of-a-friend", foaf1_uri) in list(g.namespaces()) @pytest.mark.xfail( reason=""" Automatic handling of unknown predicates not automatically registered with namespace manager NOTE: This is not a bug, but more of a feature request. """ ) def test_automatic_handling_of_unknown_predicates(): # AUTOMATIC HANDLING OF UNKNOWN PREDICATES """ Automatic handling of unknown predicates ----------------------------------------- As a programming convenience, a namespace binding is automatically created when :class:`rdflib.term.URIRef` predicates are added to the graph. """ g = Graph(bind_namespaces="none") g.add((tarek, URIRef("http://xmlns.com/foaf/0.1/name"), Literal("Tarek"))) assert len(list(g.namespaces())) > 0 def test_automatic_handling_of_unknown_predicates_only_effected_after_serialization(): g = Graph(bind_namespaces="none") g.add((tarek, URIRef("http://xmlns.com/foaf/0.1/name"), Literal("Tarek"))) assert "@prefix ns1: ." in g.serialize(format="n3") assert len(list(g.namespaces())) > 0 assert ("ns1", URIRef("http://xmlns.com/foaf/0.1/")) in list(g.namespaces()) def test_multigraph_bindings(): data = """@prefix friend-of-a-friend: . friend-of-a-friend:name "Bob" . """ store = Memory() g1 = Graph(store, identifier=context1, bind_namespaces="none") g1.bind("foaf", FOAF1) assert list(g1.namespaces()) == [("foaf", foaf1_uri)] assert list(store.namespaces()) == [("foaf", foaf1_uri)] g1.add((tarek, FOAF1.name, Literal("tarek"))) assert list(store.namespaces()) == [("foaf", foaf1_uri)] g2 = Graph(store, identifier=context2, bind_namespaces="none") g2.parse(data=data, format="n3") # The parser-caused rebind is in the underlying store and all objects # that use the store see the changed binding: assert list(store.namespaces()) == [("friend-of-a-friend", foaf1_uri)] assert list(g1.namespaces()) == [("friend-of-a-friend", foaf1_uri)] # Including newly-created objects that use the store cg = ConjunctiveGraph(store=store) cg.namespace_manager = NamespaceManager(cg, bind_namespaces="core") assert ("foaf", foaf1_uri) not in list(cg.namespaces()) assert ("friend-of-a-friend", foaf1_uri) in list(cg.namespaces()) assert len(list(g1.namespaces())) == 6 assert len(list(g2.namespaces())) == 6 assert len(list(cg.namespaces())) == 6 assert len(list(store.namespaces())) == 6 cg.store.add_graph(g1) cg.store.add_graph(g2) assert "@prefix friend-of-a-friend: " in cg.serialize( format="n3" ) # In the notation3 format, the statement asserting tarek's name # now references the changed prefix: assert ' friend-of-a-friend:name "tarek" .' in cg.serialize( format="n3" ) # Add foaf2 binding if not already bound cg.bind("foaf2", FOAF2, override=False) assert ("foaf2", foaf2_uri) in list(cg.namespaces()) # Impose foaf binding ... if not already bound cg.bind("foaf", FOAF1, override=False) assert ("foaf", foaf1_uri) not in list(cg.namespaces()) def test_new_namespace_new_prefix(): g = Graph(bind_namespaces="none") g.bind("foaf", FOAF1) assert list(g.namespaces()) == [("foaf", foaf1_uri)] def test_change_prefix_override_true(): g = Graph(bind_namespaces="none") g.bind("foaf", FOAF1) assert list(g.namespaces()) == [("foaf", foaf1_uri)] g.bind("oldfoaf", FOAF1) # Changed assert list(g.namespaces()) == [("oldfoaf", foaf1_uri)] def test_change_prefix_override_false(): g = Graph(bind_namespaces="none") g.bind("foaf", FOAF1) assert list(g.namespaces()) == [("foaf", foaf1_uri)] g.bind("oldfoaf", FOAF1, override=False) # No change assert list(g.namespaces()) == [("foaf", foaf1_uri)] def test_change_namespace_override_true(): g = Graph(bind_namespaces="none") g.bind("foaf", FOAF1) assert list(g.namespaces()) == [("foaf", foaf1_uri)] g.bind("foaf", FOAF2) # Different prefix used assert list(g.namespaces()) == [("foaf", foaf1_uri), ("foaf1", foaf2_uri)] def test_change_namespace_override_false(): g = Graph(bind_namespaces="none") g.bind("foaf", FOAF1) assert list(g.namespaces()) == [("foaf", foaf1_uri)] # Different namespace so override is irrelevant in this case g.bind("foaf", FOAF2, override=False) # Different prefix used assert list(g.namespaces()) == [("foaf", foaf1_uri), ("foaf1", foaf2_uri)] def test_new_namespace_override_false(): g = Graph(bind_namespaces="none") g.bind("foaf", FOAF2) assert list(g.namespaces()) == [("foaf", foaf2_uri)] # Namespace not already bound so override is irrelevant in this case g.bind("owl", OWL, override=False) # Given prefix used assert list(g.namespaces()) == [ ("foaf", foaf2_uri), ("owl", URIRef("http://www.w3.org/2002/07/owl#")), ] def test_change_namespace_and_prefix(): # A more extensive illustration of attempted rebinds of # `foaf` being intercepted and changed to a non-clashing # prefix of `foafN` (where N is an incrementing integer) ... g = Graph(bind_namespaces="none") g.bind("foaf", FOAF1) assert list(g.namespaces()) == [("foaf", foaf1_uri)] g.bind("foaf", FOAF2, replace=True) assert list(g.namespaces()) == [("foaf", foaf2_uri)] g.bind("foaf1", FOAF1) assert list(g.namespaces()) == [("foaf", foaf2_uri), ("foaf1", foaf1_uri)] foaf3_uri = URIRef("http://xmlns.com/foaf/3.0/") FOAF3 = Namespace("http://xmlns.com/foaf/3.0/") g.bind("foaf", FOAF3) assert list(g.namespaces()) == [ ("foaf", foaf2_uri), ("foaf1", foaf1_uri), ("foaf2", foaf3_uri), ] foaf4_uri = URIRef("http://xmlns.com/foaf/4.0/") FOAF4 = Namespace("http://xmlns.com/foaf/4.0/") g.bind("foaf", FOAF4) assert list(g.namespaces()) == [ ("foaf", foaf2_uri), ("foaf1", foaf1_uri), ("foaf2", foaf3_uri), ("foaf3", foaf4_uri), ]