rdf4j-python 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -42,6 +42,17 @@ class BaseClient:
42
42
  class SyncApiClient(BaseClient):
43
43
  """Synchronous API client using httpx.Client."""
44
44
 
45
+ def __init__(self, base_url: str, timeout: int = 10):
46
+ """
47
+ Initializes the SyncApiClient.
48
+
49
+ Args:
50
+ base_url (str): The base URL for the API endpoints.
51
+ timeout (int, optional): Request timeout in seconds. Defaults to 10.
52
+ """
53
+ super().__init__(base_url, timeout)
54
+ self.client = httpx.Client(timeout=self.timeout)
55
+
45
56
  def __enter__(self):
46
57
  """
47
58
  Enters the context and initializes the HTTP client.
@@ -49,7 +60,7 @@ class SyncApiClient(BaseClient):
49
60
  Returns:
50
61
  SyncApiClient: The instance of the client.
51
62
  """
52
- self.client = httpx.Client(timeout=self.timeout).__enter__()
63
+ self.client.__enter__()
53
64
  return self
54
65
 
55
66
  def __exit__(self, exc_type, exc_value, traceback):
@@ -142,6 +153,17 @@ class SyncApiClient(BaseClient):
142
153
  class AsyncApiClient(BaseClient):
143
154
  """Asynchronous API client using httpx.AsyncClient."""
144
155
 
156
+ def __init__(self, base_url: str, timeout: int = 10):
157
+ """
158
+ Initializes the AsyncApiClient.
159
+
160
+ Args:
161
+ base_url (str): The base URL for the API endpoints.
162
+ timeout (int, optional): Request timeout in seconds. Defaults to 10.
163
+ """
164
+ super().__init__(base_url, timeout)
165
+ self.client = httpx.AsyncClient(timeout=self.timeout)
166
+
145
167
  async def __aenter__(self):
146
168
  """
147
169
  Enters the async context and initializes the HTTP client.
@@ -149,7 +171,7 @@ class AsyncApiClient(BaseClient):
149
171
  Returns:
150
172
  AsyncApiClient: The instance of the client.
151
173
  """
152
- self.client = await httpx.AsyncClient(timeout=self.timeout).__aenter__()
174
+ await self.client.__aenter__()
153
175
  return self
154
176
 
155
177
  async def __aexit__(self, exc_type, exc_value, traceback):
@@ -69,6 +69,7 @@ class AsyncRdf4j:
69
69
  query_solutions = og.parse_query_results(
70
70
  response.text, format=og.QueryResultsFormat.JSON
71
71
  )
72
+ assert isinstance(query_solutions, og.QuerySolutions)
72
73
  return [
73
74
  RepositoryMetadata.from_sparql_query_solution(query_solution)
74
75
  for query_solution in query_solutions
@@ -25,10 +25,21 @@ from rdf4j_python.model.term import (
25
25
  from rdf4j_python.utils.const import Rdf4jContentType
26
26
  from rdf4j_python.utils.helpers import serialize_statements
27
27
 
28
+ try:
29
+ from SPARQLWrapper import SPARQLWrapper
30
+
31
+ _has_sparql_wrapper = True
32
+ except ImportError:
33
+ _has_sparql_wrapper = False
34
+
28
35
 
29
36
  class AsyncRdf4JRepository:
30
37
  """Asynchronous interface for interacting with an RDF4J repository."""
31
38
 
39
+ _client: AsyncApiClient
40
+ _repository_id: str
41
+ _sparql_wrapper: Optional["SPARQLWrapper"] = None
42
+
32
43
  def __init__(self, client: AsyncApiClient, repository_id: str):
33
44
  """Initializes the repository interface.
34
45
 
@@ -39,32 +50,47 @@ class AsyncRdf4JRepository:
39
50
  self._client = client
40
51
  self._repository_id = repository_id
41
52
 
53
+ async def get_sparql_wrapper(self) -> "SPARQLWrapper":
54
+ """Returns the SPARQLWrapper for the repository.
55
+
56
+ Returns:
57
+ SPARQLWrapper: The SPARQLWrapper for the repository.
58
+ """
59
+ if not _has_sparql_wrapper:
60
+ raise ImportError(
61
+ "SPARQLWrapper is not installed. Please install it with `pip install rdf4j-python[sparqlwrapper]`"
62
+ )
63
+
64
+ if self._sparql_wrapper is None:
65
+ self._sparql_wrapper = SPARQLWrapper(
66
+ f"{self._client.get_base_url()}/repositories/{self._repository_id}"
67
+ )
68
+ return self._sparql_wrapper
69
+
42
70
  async def query(
43
71
  self,
44
72
  sparql_query: str,
45
73
  infer: bool = True,
46
- accept: Rdf4jContentType = Rdf4jContentType.SPARQL_RESULTS_JSON,
47
- ):
74
+ ) -> og.QuerySolutions | og.QueryBoolean:
48
75
  """Executes a SPARQL SELECT query.
49
76
 
50
77
  Args:
51
78
  sparql_query (str): The SPARQL query string.
52
79
  infer (bool): Whether to include inferred statements. Defaults to True.
53
- accept (Rdf4jContentType): The expected response format.
54
80
 
55
81
  Returns:
56
- dict or str: Parsed JSON results or raw response text.
82
+ og.QuerySolutions | og.QueryBoolean: Parsed query results.
57
83
  """
58
84
  path = f"/repositories/{self._repository_id}"
59
85
  params = {"query": sparql_query, "infer": str(infer).lower()}
60
- headers = {"Accept": accept}
86
+ headers = {"Accept": Rdf4jContentType.SPARQL_RESULTS_JSON}
61
87
  response = await self._client.get(path, params=params, headers=headers)
62
88
  self._handle_repo_not_found_exception(response)
63
- if "json" in response.headers.get("Content-Type", ""):
64
- return response.json()
65
- return response.text
89
+ return og.parse_query_results(response.text, format=og.QueryResultsFormat.JSON)
66
90
 
67
- async def update(self, sparql_update: str):
91
+ async def update(
92
+ self, sparql_update_query: str, content_type: Rdf4jContentType
93
+ ) -> None:
68
94
  """Executes a SPARQL UPDATE command.
69
95
 
70
96
  Args:
@@ -74,11 +100,16 @@ class AsyncRdf4JRepository:
74
100
  RepositoryNotFoundException: If the repository doesn't exist.
75
101
  httpx.HTTPStatusError: If the update fails.
76
102
  """
103
+ # SPARQL UPDATE operations return HTTP 204 No Content on success.
104
+ # No result data is returned as per SPARQL 1.1 UPDATE specification.
77
105
  path = f"/repositories/{self._repository_id}/statements"
78
- headers = {"Content-Type": Rdf4jContentType.SPARQL_UPDATE.value}
79
- response = await self._client.post(path, data=sparql_update, headers=headers)
106
+ headers = {"Content-Type": content_type}
107
+ response = await self._client.post(
108
+ path, content=sparql_update_query, headers=headers
109
+ )
80
110
  self._handle_repo_not_found_exception(response)
81
- response.raise_for_status()
111
+ if response.status_code != httpx.codes.NO_CONTENT:
112
+ raise RepositoryUpdateException(f"Failed to update: {response.text}")
82
113
 
83
114
  async def get_namespaces(self):
84
115
  """Retrieves all namespaces in the repository.
@@ -97,6 +128,7 @@ class AsyncRdf4JRepository:
97
128
  query_solutions = og.parse_query_results(
98
129
  response.text, format=og.QueryResultsFormat.JSON
99
130
  )
131
+ assert isinstance(query_solutions, og.QuerySolutions)
100
132
  return [
101
133
  Namespace.from_sparql_query_solution(query_solution)
102
134
  for query_solution in query_solutions