Skip to content

Commit

Permalink
fix: propagate exceptions from SPARQL TSV result parser
Browse files Browse the repository at this point in the history
This patch changes the `TSVResultParser` to not eat exceptions, as this
results in silent failures that are hard to debug, and in general all
functions should either do what they say they will or create an error
indication to the caller, which in python is done with exceptions.

Fixes:
- RDFLib#1477
  • Loading branch information
aucampia committed Apr 14, 2022
1 parent cdaee27 commit 34aa7f4
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 24 deletions.
42 changes: 18 additions & 24 deletions rdflib/plugins/sparql/results/tsvresults.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,30 +68,24 @@ def parse(self, source, content_type=None):
# if reading from source returns bytes do utf-8 decoding
source = codecs.getreader("utf-8")(source)

try:
r = Result("SELECT")

header = source.readline()

r.vars = list(HEADER.parseString(header.strip(), parseAll=True))
r.bindings = []
while True:
line = source.readline()
if not line:
break
line = line.strip("\n")
if line == "":
continue

row = ROW.parseString(line, parseAll=True)
r.bindings.append(dict(zip(r.vars, (self.convertTerm(x) for x in row))))

return r

except ParseException as err:
print(err.line)
print(" " * (err.column - 1) + "^")
print(err)
r = Result("SELECT")

header = source.readline()

r.vars = list(HEADER.parseString(header.strip(), parseAll=True))
r.bindings = []
while True:
line = source.readline()
if not line:
break
line = line.strip("\n")
if line == "":
continue

row = ROW.parseString(line, parseAll=True)
r.bindings.append(dict(zip(r.vars, (self.convertTerm(x) for x in row))))

return r

def convertTerm(self, t):
if t is NONE_VALUE:
Expand Down
53 changes: 53 additions & 0 deletions test/test_sparql/test_result.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import inspect
import logging
from io import StringIO
from typing import Mapping, Sequence, Type, Union

import pytest
from pyparsing import ParseException

from rdflib.query import Result
from rdflib.term import Identifier, Literal, Variable

BindingsType = Sequence[Mapping[Variable, Identifier]]
ParseOutcomeType = Union[BindingsType, Type[Exception]]


@pytest.mark.parametrize(
("data", "format", "parse_outcome"),
[
pytest.param(
"a\n1",
"csv",
[{Variable("a"): Literal("1")}],
id="csv-okay-1c1r",
),
pytest.param(
'?a\n"1"',
"tsv",
[{Variable("a"): Literal("1")}],
id="tsv-okay-1c1r",
),
pytest.param(
"1,2,3\nhttp://example.com",
"tsv",
ParseException,
id="tsv-invalid",
),
],
)
def test_select_result_parse(
data: str, format: str, parse_outcome: ParseOutcomeType
) -> None:
"""
Round tripping of a select query through the serializer and parser of a
specific format results in an equivalent result object.
"""
logging.debug("data = %s", data)

if inspect.isclass(parse_outcome) and issubclass(parse_outcome, Exception):
with pytest.raises(parse_outcome):
parsed_result = Result.parse(StringIO(data), format=format)
else:
parsed_result = Result.parse(StringIO(data), format=format)
assert parse_outcome == parsed_result.bindings

0 comments on commit 34aa7f4

Please sign in to comment.