Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documented return value of the Triplestore.query() and added a test for it #9

Merged
merged 11 commits into from
Oct 25, 2022
17 changes: 17 additions & 0 deletions tests/test_triplestore.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ def test_triplestore(
ts_as_turtle = store.serialize(format="turtle")
assert ts_as_turtle == expected_function_triplestore

# Test SPARQL query
rows = store.query("SELECT ?s ?o WHERE { ?s rdfs:subClassOf ?o }")
assert len(rows) == 3
rows.sort() # ensure consistent ordering of rows
assert rows[0] == (
"http://example.com/onto#AnotherConcept",
"http://www.w3.org/2002/07/owl#Thing",
)
assert rows[1] == (
"http://example.com/onto#MyConcept",
"http://www.w3.org/2002/07/owl#Thing",
)
assert rows[2] == (
"http://example.com/onto#Sum",
"http://www.w3.org/2002/07/owl#Thing",
)


def test_backend_rdflib(expected_function_triplestore: str) -> None:
"""Specifically test the rdflib backend Triplestore.
Expand Down
36 changes: 31 additions & 5 deletions tripper/backends/ontopy.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

if TYPE_CHECKING: # pragma: no cover
from collections.abc import Sequence
from typing import Generator, Optional, Union
from typing import Generator, List, Optional, Union

from rdflib.query import Result

from tripper.triplestore import Triple

Expand Down Expand Up @@ -202,8 +204,21 @@ def serialize(
os.remove(filename)
return None

def query(self, query_object, native=True, **kwargs):
"""SPARQL query."""
def query(self, query_object, native=True, **kwargs) -> "Union[List, Result]":
"""SPARQL query.

Parameters:
query_object: String with the SPARQL query.
native: Whether or not to use EMMOntoPy/Owlready2 or RDFLib.
kwargs: Keyword arguments passed to rdflib.Graph.query().

Returns:
SPARQL query results.

"""
if TYPE_CHECKING:
res: "Union[List, Result]"

if native:
res = self.onto.world.sparql(query_object)
else:
Expand All @@ -212,8 +227,19 @@ def query(self, query_object, native=True, **kwargs):
# TODO: Convert result to expected type
return res

def update(self, update_object, native=True, **kwargs):
"""Update triplestore with SPARQL."""
def update(self, update_object, native=True, **kwargs) -> None:
"""Update triplestore with SPARQL.

Parameters:
update_object: String with the SPARQL query.
native: Whether or not to use EMMOntoPy/Owlready2 or RDFLib.
kwargs: Keyword arguments passed to rdflib.Graph.update().

Note:
This method is intended for INSERT and DELETE queries. Use
the query() method for SELECT queries.

"""
if native:
self.onto.world.sparql(update_object)
else:
Expand Down
33 changes: 26 additions & 7 deletions tripper/backends/rdflib.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

if TYPE_CHECKING: # pragma: no cover
from collections.abc import Sequence
from typing import Generator, Union
from typing import Generator, List, Tuple, Union

from tripper.triplestore import Triple

Expand Down Expand Up @@ -115,13 +115,32 @@ def serialize(
return result if isinstance(result, str) else result.decode()
return None

def query(self, query_object, **kwargs):
"""SPARQL query."""
# TODO: convert to returned object
return self.graph.query(query_object=query_object, **kwargs)
def query(self, query_object, **kwargs) -> "List[Tuple[str, ...]]":
"""SPARQL query.

def update(self, update_object, **kwargs):
"""Update triplestore with SPARQL."""
Parameters:
query_object: String with the SPARQL query.
kwargs: Keyword arguments passed to rdflib.Graph.query().

Returns:
List of tuples of IRIs for each matching row.

"""
rows = self.graph.query(query_object=query_object, **kwargs)
return [tuple(str(v) for v in row) for row in rows]
jesper-friis marked this conversation as resolved.
Show resolved Hide resolved

def update(self, update_object, **kwargs) -> None:
"""Update triplestore with SPARQL.

Parameters:
update_object: String with the SPARQL query.
kwargs: Keyword arguments passed to rdflib.Graph.update().

Note:
This method is intended for INSERT and DELETE queries. Use
the query() method for SELECT queries.

"""
return self.graph.update(update_object=update_object, **kwargs)

def bind(self, prefix: str, namespace: str):
Expand Down
73 changes: 59 additions & 14 deletions tripper/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,27 @@ class ITriplestore(Protocol):

```python

def __init__(self, base_iri=None, **kwargs)
def __init__(self, base_iri: str = None, **kwargs):
"""Initialise triplestore.

def parse(self, source=None, location=None, data=None, format=None,
**kwargs):
Arguments:
base_iri: Optional base IRI to initiate the triplestore from.
kwargs: Additional keyword arguments passed to the backend.
"""

def parse(
self,
source: Union[str, Path, IO] = None,
location: str = None,
data: str = None,
format: str = None,
**kwargs
):
"""Parse source and add the resulting triples to triplestore.

The source is specified using one of `source`, `location` or `data`.

Parameters:
Arguments:
source: File-like object or file name.
location: String with relative or absolute URL to source.
data: String containing the data to be parsed.
Expand All @@ -40,10 +52,15 @@ def parse(self, source=None, location=None, data=None, format=None,
the parsing.
"""

def serialize(self, destination=None, format='xml', **kwargs)
def serialize(
self,
destination: Union[str, Path, IO] = None,
format: str ='xml',
**kwargs
):
"""Serialise to destination.

Parameters:
Arguments:
destination: File name or object to write to. If None, the
serialisation is returned.
format: Format to serialise as. Supported formats, depends on
Expand All @@ -55,19 +72,36 @@ def serialize(self, destination=None, format='xml', **kwargs)
Serialised string if `destination` is None.
"""

def query(self, query_object, **kwargs)
"""SPARQL query."""
def query(self, query_object: str, **kwargs) -> List:
"""SPARQL query.

Arguments:
query_object: String with the SPARQL query.
kwargs: Additional backend-specific keyword arguments.

Returns:
List of tuples of IRIs for each matching row.
"""

def update(self, update_object: str, **kwargs):
"""Update triplestore with SPARQL.

def update(self, update_object, **kwargs)
"""Update triplestore with SPARQL."""
Arguments:
query_object: String with the SPARQL query.
kwargs: Additional backend-specific keyword arguments.

def bind(self, prefix: str, namespace: str)
Note:
This method is intended for INSERT and DELETE queries. Use
the query() method for SELECT queries.
"""

def bind(self, prefix: str, namespace: str) -> Namespace:
"""Bind prefix to namespace.

Should only be defined if the backend supports namespaces.
"""

def namespaces(self) -> dict
def namespaces(self) -> dict:
"""Returns a dict mapping prefixes to namespaces.

Should only be defined if the backend supports namespaces.
Expand All @@ -78,10 +112,21 @@ def namespaces(self) -> dict
'''

def triples(self, triple: "Triple") -> "Generator":
"""Returns a generator over matching triples."""
"""Returns a generator over matching triples.

Arguments:
triple: A `(s, p, o)` tuple where `s`, `p` and `o` should
either be None (matching anything) or an exact IRI to
match.
"""

def add_triples(self, triples: "Sequence[Triple]"):
"""Add a sequence of triples."""
"""Add a sequence of triples.

Arguments:
triples: A sequence of `(s, p, o)` tuples to add to the
triplestore.
"""

def remove(self, triple: "Triple"):
"""Remove all matching triples from the backend."""
52 changes: 45 additions & 7 deletions tripper/triplestore.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@

if TYPE_CHECKING: # pragma: no cover
from collections.abc import Mapping
from typing import Any, Callable, Dict, Generator, Tuple, Union
from typing import Any, Callable, Dict, Generator, List, Tuple, Union

from rdflib.query import Result as rdflibResult

Triple = Tuple[Union[str, None], Union[str, None], Union[str, None]]

Expand Down Expand Up @@ -392,11 +394,22 @@ def __init__(self, backend: str, base_iri: str = None, **kwargs):
# Methods implemented by backend
# ------------------------------
def triples(self, triple: "Triple") -> "Generator":
"""Returns a generator over matching triples."""
"""Returns a generator over matching triples.

Arguments:
triple: A `(s, p, o)` tuple where `s`, `p` and `o` should
either be None (matching anything) or an exact IRI to
match.
"""
return self.backend.triples(triple)

def add_triples(self, triples: "Sequence[Triple]"):
"""Add a sequence of triples."""
"""Add a sequence of triples.

Arguments:
triples: A sequence of `(s, p, o)` tuples to add to the
triplestore.
"""
self.backend.add_triples(triples)

def remove(self, triple: "Triple"):
Expand Down Expand Up @@ -447,13 +460,38 @@ def serialize(
self._check_method("serialize")
return self.backend.serialize(destination=destination, format=format, **kwargs)

def query(self, query_object, **kwargs):
"""SPARQL query."""
def query(
self, query_object, **kwargs
) -> "Union[rdflibResult, List, List[Tuple[str, ...]]]":
"""SPARQL query.

Parameters:
query_object: String with the SPARQL query.
kwargs: Keyword arguments passed to the backend query() method.

Returns:
List of tuples of IRIs for each matching row.

Note:
This method is intended for SELECT queries. Use
the update() method for INSERT and DELETE queries.

"""
self._check_method("query")
return self.backend.query(query_object=query_object, **kwargs)

def update(self, update_object, **kwargs):
"""Update triplestore with SPARQL."""
def update(self, update_object, **kwargs) -> None:
"""Update triplestore with SPARQL.

Parameters:
update_object: String with the SPARQL query.
kwargs: Keyword arguments passed to the backend update() method.

Note:
This method is intended for INSERT and DELETE queries. Use
the query() method for SELECT queries.

"""
self._check_method("update")
return self.backend.update(update_object=update_object, **kwargs)

Expand Down