Skip to content

Commit

Permalink
Some minor changes
Browse files Browse the repository at this point in the history
- I moved the Turtle and JSON-LD tests to `test/data/variants` as this
  allows for further expansion and uses a shared processor which
  somewhat simplifies testing.
- I changed the JSON-LD to be compatible with Jena's riot and jsonld-cli
  as these do not allow for object valued `@type` directives. I also
  think this is in line with the JSON-LD spec.
- I changed the conditional skips to xfails, as I think this is most
  appropriate in this case, we can provide a strict flag before version
  7.0, and the strict flag's default value can be varied from release to
  release.
  • Loading branch information
aucampia committed May 17, 2022
1 parent d77d894 commit c387c78
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 169 deletions.
10 changes: 10 additions & 0 deletions test/data/variants/forward_slash-asserts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"quad_count": 4,
"exact_match": true,
"has_subject_iris": [
"http://example.org/kb/individual-a",
"http://example.org/kb/individual-b",
"http://example.org/ontology/core/MyClassA",
"http://example.org/ontology/core/MyClassB"
]
}
33 changes: 33 additions & 0 deletions test/data/variants/forward_slash-variant-prefixed.jsonld
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"@context": {
"ex": "http://example.org/ontology/",
"kb": "http://example.org/kb/",
"owl": "http://www.w3.org/2002/07/owl#"
},
"@x-comment": [

This comment has been minimized.

Copy link
@ajnelson-nist

ajnelson-nist May 17, 2022

Contributor

I've never seen this construct before, and I don't see where the IANA style of prefixing "x-" is allowed in the JSON-LD spec. I don't work with named graphs, so I don't know what the best way is to attach a comment to a graph. If this doesn't cause breakage, I'm ok with it staying, but I want to log (just in this comment is fine) that I don't know how valid @x-comment is.

"The JSON-LD spec does not provide a grammar production rule set in,",
"EBNF. However, the section on compact IRIs indicates that an IRI can",
"be prefixed at any point that would not result in a suffix starting",
"with \"//\". Hence, an unpaired forward slash, as a legal character of",
"an IRI, can appear in the suffix component of a compact IRI.",
"https://json-ld.org/spec/latest/json-ld/#compact-iris"
],
"@graph": [
{
"@id": "kb:individual-a",
"@type": "ex:core/MyClassA"
},
{
"@id": "ex:core/MyClassA",
"@type": "owl:Class"
},
{
"@id": "kb:individual-b",
"@type": "ex:core/MyClassB"
},
{
"@id": "ex:core/MyClassB",
"@type": "owl:Class"
}
]
}
18 changes: 18 additions & 0 deletions test/data/variants/forward_slash-variant-prefixed.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@prefix ex: <http://example.org/ontology/> .
@prefix kb: <http://example.org/kb/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .

# Spell a class name with prefixing, but have the prefixing NOT include
# one of the forward-slashed path components.
# The forward slash must be escaped, according to Turtle grammar
# production rules grammar rules including and between PN_LOCAL and
# PN_LOCAL_ESC.
# https://www.w3.org/TR/turtle/#sec-grammar-grammar

ex:core\/MyClassA a owl:Class .

kb:individual-a a ex:core\/MyClassA .

ex:core\/MyClassB a owl:Class .

kb:individual-b a ex:core\/MyClassB .
24 changes: 24 additions & 0 deletions test/data/variants/forward_slash.jsonld
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"@context": {
"kb": "http://example.org/kb/",
"owl": "http://www.w3.org/2002/07/owl#"
},
"@graph": [
{
"@id": "kb:individual-a",
"@type": "http://example.org/ontology/core/MyClassA"
},
{
"@id": "http://example.org/ontology/core/MyClassA",
"@type": "owl:Class"
},
{
"@id": "kb:individual-b",
"@type": "http://example.org/ontology/core/MyClassB"
},
{
"@id": "http://example.org/ontology/core/MyClassB",
"@type": "owl:Class"
}
]
}
4 changes: 4 additions & 0 deletions test/data/variants/forward_slash.nt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<http://example.org/ontology/core/MyClassA> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> .
<http://example.org/kb/individual-a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/ontology/core/MyClassA> .
<http://example.org/ontology/core/MyClassB> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> .
<http://example.org/kb/individual-b> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/ontology/core/MyClassB> .
10 changes: 10 additions & 0 deletions test/data/variants/forward_slash.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@prefix kb: <http://example.org/kb/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .

<http://example.org/ontology/core/MyClassA> a owl:Class .

kb:individual-a a <http://example.org/ontology/core/MyClassA> .

<http://example.org/ontology/core/MyClassB> a owl:Class .

kb:individual-b a <http://example.org/ontology/core/MyClassB> .
14 changes: 14 additions & 0 deletions test/test_graph/test_variants.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,26 @@ class GraphAsserts:

quad_count: Optional[int] = None
exact_match: bool = False
has_subject_iris: Optional[List[str]] = None

def check(
self, first_graph: Optional[ConjunctiveGraph], graph: ConjunctiveGraph
) -> None:
"""
if `first_graph` is `None` then this is the first check before any
other graphs have been processed.
"""
if self.quad_count is not None:
assert self.quad_count == len(list(graph.quads()))
if first_graph is not None and self.exact_match:
GraphHelper.assert_quad_sets_equals(first_graph, graph)
if first_graph is None and self.has_subject_iris is not None:
subjects_iris = {
f"{subject}"
for subject in graph.subjects()
if isinstance(subject, URIRef)
}
assert set(self.has_subject_iris) == subjects_iris


@dataclass(order=True)
Expand Down Expand Up @@ -219,6 +231,8 @@ def test_variants(graph_variant: GraphVariants) -> None:
assert len(graph_variant.variants) > 0
first_graph: Optional[ConjunctiveGraph] = None
first_path: Optional[Path] = None
logging.debug("graph_variant.asserts = %s", graph_variant.asserts)

for variant_key, variant_path in graph_variant.variants.items():
logging.debug("variant_path = %s", variant_path)
format = guess_format(variant_path.name, fmap=SUFFIX_FORMAT_MAP)
Expand Down
187 changes: 18 additions & 169 deletions test/test_sparql/test_forward_slash_escapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,99 +19,15 @@
"mime:application/json".
"""

from typing import List, Set, Tuple
from test.data import TEST_DATA_DIR
from test.utils.graph import cached_graph
from typing import Set

import pytest

import rdflib
from rdflib import Graph
from rdflib.plugins.sparql.processor import prepareQuery
from rdflib.plugins.sparql.sparql import Query
from rdflib.term import Node

# Determine version to delay a test until a version > 6 is being
# prepared for release. A 7-point-0-anything, alpha or beta designation
# should enable some further tests backwards-incompatible with version 6.
# Treat rdflib.__version__ like it can be compiled into a version_info
# tuple similar to sys.version_info.
# TODO: During the release of version 7, this set of version-detection
# code and its corresponding skipifs can be deleted.
_rdflib_version_info_strs: List[str] = rdflib.__version__.split(".")
_is_version_7_started = False
if _rdflib_version_info_strs[0].isdigit():
if int(_rdflib_version_info_strs[0]) >= 7:
_is_version_7_started = True

# Note that the data and query strings are Python raw strings, so
# backslashes won't be escape characters to Python.
# The "*_expanded" variables eschew prefixing.

# Spell a class name without prefixing.
turtle_data_expanded = r"""
@prefix kb: <http://example.org/kb/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
<http://example.org/ontology/core/MyClassA> a owl:Class .
kb:individual-a a <http://example.org/ontology/core/MyClassA> .
"""

# Spell a class name with prefixing, but have the prefixing NOT include
# one of the forward-slashed path components.
# The forward slash must be escaped, according to Turtle grammar
# production rules grammar rules including and between PN_LOCAL and
# PN_LOCAL_ESC.
# https://www.w3.org/TR/turtle/#sec-grammar-grammar
turtle_data_prefixed = r"""
@prefix ex: <http://example.org/ontology/> .
@prefix kb: <http://example.org/kb/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
ex:core\/MyClassB a owl:Class .
kb:individual-b a ex:core\/MyClassB .
"""

# This data is an equivalant graph with turtle_data_expanded.
jsonld_data_expanded = r"""
{
"@context": {
"kb": "http://example.org/kb/",
"owl": "http://www.w3.org/2002/07/owl#"
},
"@graph": {
"@id": "kb:individual-a",
"@type": {
"@id": "http://example.org/ontology/core/MyClassA",
"@type": "owl:Class"
}
}
}
"""

# This data is an equivalant graph with turtle_data_prefixed.
# The JSON-LD spec does not provide a grammar production rule set in
# EBNF. However, the section on compact IRIs indicates that an IRI can
# be prefixed at any point that would not result in a suffix starting
# with "//". Hence, an unpaired forward slash, as a legal character of
# an IRI, can appear in the suffix component of a compact IRI.
# https://json-ld.org/spec/latest/json-ld/#compact-iris
jsonld_data_prefixed = r"""
{
"@context": {
"ex": "http://example.org/ontology/",
"kb": "http://example.org/kb/",
"owl": "http://www.w3.org/2002/07/owl#"
},
"@graph": {
"@id": "kb:individual-b",
"@type": {
"@id": "ex:core/MyClassB",
"@type": "owl:Class"
}
}
}
"""

query_string_expanded = r"""
SELECT ?nIndividual
Expand All @@ -132,66 +48,16 @@
?nIndividual a ex:core\/MyClassB .
}"""

PN_LOCAL_BACKSLASH_XFAIL_REASON = """\
Contrary to the ratiried SPARQL 1.1.

This comment has been minimized.

Copy link
@ajnelson-nist

ajnelson-nist May 17, 2022

Contributor

ratified?

This comment has been minimized.

Copy link
@ajnelson-nist

ajnelson-nist May 17, 2022

Contributor

also, is that period and line break intentional?

This comment has been minimized.

Copy link
@aucampia

aucampia May 17, 2022

Author Member

also, is that period and line break intentional?

Line break yes, to keep the output wrapped and easier to read, but period no. Fixing.

This comment has been minimized.

Copy link
@aucampia

aucampia May 17, 2022

Author Member

ratified?

Indeed, fixing.

This comment has been minimized.

Copy link
@aucampia

aucampia May 17, 2022

Author Member

Actually I re-wrapped it now and the newline is gone.

grammar, the RDFlib SPARQL propcessor accepts backslashes as part of
PN_LOCAL which it treats as escape charachters.
def _test_parse_matches(turtle_data: str, jsonld_data: str) -> None:
"""
Confirm that the two concrete syntaxes parse to the same set of
triples.
"""
g_turtle = Graph()
g_jsonld = Graph()
There should be a way to instruct the SPARQL parser to operate in strict
mode, and in strict mode backslashes should not be permitted in PN_LOCAL.
g_turtle.parse(data=turtle_data_expanded, format="turtle")
g_jsonld.parse(data=jsonld_data_expanded, format="json-ld")

triples_turtle: Set[Tuple[Node, Node, Node]] = {
x for x in g_turtle.triples((None, None, None))
}
triples_jsonld: Set[Tuple[Node, Node, Node]] = {
x for x in g_jsonld.triples((None, None, None))
}

assert triples_turtle == triples_jsonld


def test_parse_matches_expanded() -> None:
_test_parse_matches(turtle_data_expanded, jsonld_data_expanded)


def test_parse_matches_prefixed() -> None:
_test_parse_matches(turtle_data_prefixed, jsonld_data_prefixed)


def _test_escapes_to_iri(graph: Graph) -> None:
"""
Confirm all triple-subjects within the two data graphs are found.
"""
expected: Set[str] = {
"http://example.org/kb/individual-a",
"http://example.org/kb/individual-b",
"http://example.org/ontology/core/MyClassA",
"http://example.org/ontology/core/MyClassB",
}
computed: Set[str] = set()

for triple in graph.triples((None, None, None)):
computed.add(str(triple[0]))

assert expected == computed


def test_escapes_to_iri_turtle() -> None:
graph = Graph()
graph.parse(data=turtle_data_expanded, format="turtle")
graph.parse(data=turtle_data_prefixed, format="turtle")
_test_escapes_to_iri(graph)


def test_escapes_to_iri_jsonld() -> None:
graph = Graph()
graph.parse(data=jsonld_data_expanded, format="json-ld")
graph.parse(data=jsonld_data_prefixed, format="json-ld")
_test_escapes_to_iri(graph)
See https://github.com/RDFLib/rdflib/issues/1871\

This comment has been minimized.

Copy link
@ajnelson-nist

ajnelson-nist May 17, 2022

Contributor

Is this trailing backslash intentional, to end the triple-quoted string at the beginning of the next line? I'm fine if this was intentional.

This comment has been minimized.

Copy link
@aucampia

aucampia May 17, 2022

Author Member

I placed it there to avoid pytest producing more newlines than are needed for the output to be readable, but maybe this is somewhat overpinning, I will remove it.

"""


def _test_query_prepares(query_string: str) -> None:
Expand All @@ -215,10 +81,7 @@ def test_query_prepares_expanded() -> None:
_test_query_prepares(query_string_expanded)


@pytest.mark.skipif(
not _is_version_7_started,
reason="query failure detection delayed until rdflib version 7",
)
@pytest.mark.xfail(reason=PN_LOCAL_BACKSLASH_XFAIL_REASON)

This comment has been minimized.

Copy link
@ajnelson-nist

ajnelson-nist May 17, 2022

Contributor

Yes, thank you for making this a shared string.

def test_query_prepares_prefixed() -> None:
with pytest.raises(ValueError):
_test_query_prepares(query_string_prefixed)
Expand Down Expand Up @@ -256,36 +119,22 @@ def _test_escapes_and_query(


def test_escapes_and_query_turtle_expanded() -> None:
graph = Graph()
graph.parse(data=turtle_data_expanded, format="turtle")
graph.parse(data=turtle_data_prefixed, format="turtle")
graph = cached_graph((TEST_DATA_DIR / "variants/forward_slash.ttl",))
_test_escapes_and_query(graph, query_string_expanded, True)


@pytest.mark.skipif(
not _is_version_7_started,
reason="query failure detection delayed until rdflib version 7",
)
@pytest.mark.xfail(reason=PN_LOCAL_BACKSLASH_XFAIL_REASON, raises=AssertionError)
def test_escapes_and_query_turtle_prefixed() -> None:
graph = Graph()
graph.parse(data=turtle_data_expanded, format="turtle")
graph.parse(data=turtle_data_prefixed, format="turtle")
graph = cached_graph((TEST_DATA_DIR / "variants/forward_slash.ttl",))
_test_escapes_and_query(graph, query_string_prefixed, False)


def test_escapes_and_query_jsonld_expanded() -> None:
graph = Graph()
graph.parse(data=jsonld_data_expanded, format="json-ld")
graph.parse(data=jsonld_data_prefixed, format="json-ld")
graph = cached_graph((TEST_DATA_DIR / "variants/forward_slash.jsonld",))
_test_escapes_and_query(graph, query_string_expanded, True)


@pytest.mark.skipif(
not _is_version_7_started,
reason="query failure detection delayed until rdflib version 7",
)
@pytest.mark.xfail(reason=PN_LOCAL_BACKSLASH_XFAIL_REASON, raises=AssertionError)
def test_escapes_and_query_jsonld_prefixed() -> None:
graph = Graph()
graph.parse(data=jsonld_data_expanded, format="json-ld")
graph.parse(data=jsonld_data_prefixed, format="json-ld")
graph = cached_graph((TEST_DATA_DIR / "variants/forward_slash.jsonld",))
_test_escapes_and_query(graph, query_string_prefixed, False)

1 comment on commit c387c78

@ajnelson-nist
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm overall fine with this commit. It looks like there should be at least one follow-on commit to fix typographical issues. Please let me know what commit I should merge into my branch, if I still need to make that merge. (I continue to not understand if checking the "Allow edits and access to secrets by maintainers" box lets you actually do that.)

Please sign in to comment.