diff --git a/rdfproxy/__init__.py b/rdfproxy/__init__.py index 5cde0d8..1d3478b 100644 --- a/rdfproxy/__init__.py +++ b/rdfproxy/__init__.py @@ -1,8 +1,4 @@ from rdfproxy.adapter import SPARQLModelAdapter # noqa: F401 -from rdfproxy.sparql_strategies import ( - SPARQLStrategy, # noqa: F401 - SPARQLWrapperStrategy, # noqa: F401 - HttpxStrategy, # noqa: F401 -) +from rdfproxy.sparqlwrapper import SPARQLWrapper # noqa: F401 from rdfproxy.utils._types import ConfigDict, SPARQLBinding # noqa: F401 from rdfproxy.utils.models import Page, QueryParameters # noqa: F401 diff --git a/rdfproxy/adapter.py b/rdfproxy/adapter.py index 6a66dc2..b70c06c 100644 --- a/rdfproxy/adapter.py +++ b/rdfproxy/adapter.py @@ -7,7 +7,7 @@ from rdfproxy.constructor import _QueryConstructor from rdfproxy.mapper import _ModelBindingsMapper -from rdfproxy.sparql_strategies import HttpxStrategy, SPARQLStrategy +from rdfproxy.sparqlwrapper import SPARQLWrapper from rdfproxy.utils._types import _TModelInstance from rdfproxy.utils.checkers.query_checker import check_query from rdfproxy.utils.models import Page, QueryParameters @@ -38,13 +38,12 @@ def __init__( target: str, query: str, model: type[_TModelInstance], - sparql_strategy: type[SPARQLStrategy] = HttpxStrategy, ) -> None: self._target = target self._query = check_query(query) self._model = model - self.sparql_strategy = sparql_strategy(self._target) + self.sparqlwrapper = SPARQLWrapper(self._target) logger.info("Initialized SPARQLModelAdapter.") logger.debug("Endpoint: %s", self._target) @@ -70,13 +69,13 @@ def query( logger.debug("Running items query: \n%s", items_query) - items_query_bindings: Iterator[dict] = self.sparql_strategy.query(items_query) + items_query_bindings: Iterator[dict] = self.sparqlwrapper.query(items_query) mapper = _ModelBindingsMapper(self._model, items_query_bindings) items: list[_TModelInstance] = mapper.get_models() logger.debug("Running count query: \n%s", count_query) - count_query_bindings: Iterator[dict] = self.sparql_strategy.query(count_query) + count_query_bindings: Iterator[dict] = self.sparqlwrapper.query(count_query) total: int = int(next(count_query_bindings)["cnt"]) pages: int = math.ceil(total / query_parameters.size) diff --git a/rdfproxy/sparql_strategies.py b/rdfproxy/sparql_strategies.py deleted file mode 100644 index da26d41..0000000 --- a/rdfproxy/sparql_strategies.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Strategy classes for SPARQL query functionality.""" - -import abc -from collections.abc import Iterator -from typing import cast - -from SPARQLWrapper import JSON, QueryResult, SPARQLWrapper -import httpx - - -class SPARQLStrategy(abc.ABC): - def __init__(self, endpoint: str): - self.endpoint = endpoint - - @abc.abstractmethod - def query(self, sparql_query: str) -> Iterator[dict[str, str]]: - raise NotImplementedError # pragma: no cover - - @staticmethod - def _get_bindings_from_bindings_dict(bindings_dict: dict) -> Iterator[dict]: - bindings = map( - lambda binding: {k: v["value"] for k, v in binding.items()}, - bindings_dict["results"]["bindings"], - ) - return bindings - - -class SPARQLWrapperStrategy(SPARQLStrategy): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self._sparql_wrapper = SPARQLWrapper(self.endpoint) - self._sparql_wrapper.setReturnFormat(JSON) - - def query(self, sparql_query: str) -> Iterator[dict[str, str]]: - self._sparql_wrapper.setQuery(sparql_query) - - result: QueryResult = self._sparql_wrapper.query() - # SPARQLWrapper.Wrapper.convert is not overloaded properly and needs casting - # https://github.com/RDFLib/sparqlwrapper/blob/master/SPARQLWrapper/Wrapper.py#L1135 - return self._get_bindings_from_bindings_dict(cast(dict, result.convert())) - - -class HttpxStrategy(SPARQLStrategy): - def query(self, sparql_query: str) -> Iterator[dict[str, str]]: - result: httpx.Response = self._httpx_run_sparql_query(sparql_query) - return self._get_bindings_from_bindings_dict(result.json()) - - def _httpx_run_sparql_query(self, query: str) -> httpx.Response: - data = {"output": "json", "query": query} - headers = { - "Accept": "application/sparql-results+json", - } - - response = httpx.post( - self.endpoint, - headers=headers, - data=data, - ) - - return response diff --git a/rdfproxy/sparqlwrapper.py b/rdfproxy/sparqlwrapper.py new file mode 100644 index 0000000..9f720dc --- /dev/null +++ b/rdfproxy/sparqlwrapper.py @@ -0,0 +1,37 @@ +from collections.abc import Iterator + +import httpx + + +class SPARQLWrapper: + """Simple httpx-based SPARQLWrapper implementaton for RDFProxy.""" + + def __init__(self, endpoint: str): + self.endpoint = endpoint + + def query(self, query: str) -> Iterator[dict[str, str]]: + """Run a SPARQL query against endpoint and return an Iterator of flat result mappings.""" + result: httpx.Response = self._httpx_run_sparql_query(query) + return self._get_bindings_from_bindings_dict(result.json()) + + @staticmethod + def _get_bindings_from_bindings_dict(bindings_dict: dict) -> Iterator[dict]: + bindings = map( + lambda binding: {k: v["value"] for k, v in binding.items()}, + bindings_dict["results"]["bindings"], + ) + return bindings + + def _httpx_run_sparql_query(self, query: str) -> httpx.Response: + data = {"output": "json", "query": query} + headers = { + "Accept": "application/sparql-results+json", + } + + response = httpx.post( + self.endpoint, + headers=headers, + data=data, + ) + + return response diff --git a/tests/tests_adapter/test_adapter_grouped_pagination.py b/tests/tests_adapter/test_adapter_grouped_pagination.py index b4c0975..70eb3d2 100644 --- a/tests/tests_adapter/test_adapter_grouped_pagination.py +++ b/tests/tests_adapter/test_adapter_grouped_pagination.py @@ -2,9 +2,8 @@ from typing import Annotated, Any, NamedTuple -import pytest - from pydantic import BaseModel +import pytest from rdfproxy import ( ConfigDict, HttpxStrategy, @@ -12,7 +11,6 @@ QueryParameters, SPARQLBinding, SPARQLModelAdapter, - SPARQLWrapperStrategy, ) @@ -59,7 +57,7 @@ class Parent(BaseModel): children: list[Child] -@pytest.fixture(params=[HttpxStrategy, SPARQLWrapperStrategy]) +@pytest.fixture(params=[HttpxStrategy]) def adapter(request): return SPARQLModelAdapter( target="https://graphdb.r11.eu/repositories/RELEVEN", @@ -69,7 +67,7 @@ def adapter(request): ) -@pytest.fixture(params=[HttpxStrategy, SPARQLWrapperStrategy]) +@pytest.fixture(params=[HttpxStrategy]) def binding_adapter(request): return SPARQLModelAdapter( target="https://graphdb.r11.eu/repositories/RELEVEN", @@ -79,7 +77,7 @@ def binding_adapter(request): ) -@pytest.fixture(params=[HttpxStrategy, SPARQLWrapperStrategy]) +@pytest.fixture(params=[HttpxStrategy]) def ungrouped_adapter(request): return SPARQLModelAdapter( target="https://graphdb.r11.eu/repositories/RELEVEN",