rdf4j-python 0.1.1a0__py3-none-any.whl → 0.1.3__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.
- rdf4j_python/__init__.py +9 -4
- rdf4j_python/_client/_client.py +156 -5
- rdf4j_python/_driver/__init__.py +2 -0
- rdf4j_python/_driver/_async_named_graph.py +76 -0
- rdf4j_python/_driver/_async_rdf4j_db.py +60 -34
- rdf4j_python/_driver/_async_repository.py +307 -25
- rdf4j_python/exception/__init__.py +5 -0
- rdf4j_python/exception/repo_exception.py +26 -5
- rdf4j_python/model/__init__.py +5 -10
- rdf4j_python/model/_namespace.py +115 -14
- rdf4j_python/model/_repository_info.py +36 -15
- rdf4j_python/model/{_repository_config.py → repository_config.py} +671 -70
- rdf4j_python/model/term.py +20 -0
- rdf4j_python/model/vocabulary.py +7 -0
- rdf4j_python/utils/__init__.py +3 -0
- rdf4j_python/utils/const.py +4 -4
- rdf4j_python/utils/helpers.py +20 -0
- {rdf4j_python-0.1.1a0.dist-info → rdf4j_python-0.1.3.dist-info}/METADATA +13 -13
- rdf4j_python-0.1.3.dist-info/RECORD +23 -0
- {rdf4j_python-0.1.1a0.dist-info → rdf4j_python-0.1.3.dist-info}/WHEEL +1 -1
- rdf4j_python/model/_base_model.py +0 -26
- rdf4j_python-0.1.1a0.dist-info/RECORD +0 -19
- {rdf4j_python-0.1.1a0.dist-info → rdf4j_python-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {rdf4j_python-0.1.1a0.dist-info → rdf4j_python-0.1.3.dist-info}/top_level.txt +0 -0
|
@@ -1,18 +1,41 @@
|
|
|
1
|
+
from typing import Iterable, Optional
|
|
2
|
+
|
|
1
3
|
import httpx
|
|
2
|
-
import
|
|
4
|
+
import pyoxigraph as og
|
|
3
5
|
|
|
4
|
-
from rdf4j_python import AsyncApiClient
|
|
6
|
+
from rdf4j_python._client import AsyncApiClient
|
|
7
|
+
from rdf4j_python._driver._async_named_graph import AsyncNamedGraph
|
|
5
8
|
from rdf4j_python.exception.repo_exception import (
|
|
6
9
|
NamespaceException,
|
|
7
10
|
RepositoryInternalException,
|
|
8
11
|
RepositoryNotFoundException,
|
|
12
|
+
RepositoryUpdateException,
|
|
13
|
+
)
|
|
14
|
+
from rdf4j_python.model import Namespace
|
|
15
|
+
from rdf4j_python.model.term import (
|
|
16
|
+
IRI,
|
|
17
|
+
Context,
|
|
18
|
+
Object,
|
|
19
|
+
Predicate,
|
|
20
|
+
Quad,
|
|
21
|
+
QuadResultSet,
|
|
22
|
+
Subject,
|
|
23
|
+
Triple,
|
|
9
24
|
)
|
|
10
|
-
from rdf4j_python.model._namespace import Namespace
|
|
11
25
|
from rdf4j_python.utils.const import Rdf4jContentType
|
|
26
|
+
from rdf4j_python.utils.helpers import serialize_statements
|
|
12
27
|
|
|
13
28
|
|
|
14
29
|
class AsyncRdf4JRepository:
|
|
30
|
+
"""Asynchronous interface for interacting with an RDF4J repository."""
|
|
31
|
+
|
|
15
32
|
def __init__(self, client: AsyncApiClient, repository_id: str):
|
|
33
|
+
"""Initializes the repository interface.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
client (AsyncApiClient): The RDF4J API client.
|
|
37
|
+
repository_id (str): The ID of the RDF4J repository.
|
|
38
|
+
"""
|
|
16
39
|
self._client = client
|
|
17
40
|
self._repository_id = repository_id
|
|
18
41
|
|
|
@@ -22,9 +45,19 @@ class AsyncRdf4JRepository:
|
|
|
22
45
|
infer: bool = True,
|
|
23
46
|
accept: Rdf4jContentType = Rdf4jContentType.SPARQL_RESULTS_JSON,
|
|
24
47
|
):
|
|
48
|
+
"""Executes a SPARQL SELECT query.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
sparql_query (str): The SPARQL query string.
|
|
52
|
+
infer (bool): Whether to include inferred statements. Defaults to True.
|
|
53
|
+
accept (Rdf4jContentType): The expected response format.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
dict or str: Parsed JSON results or raw response text.
|
|
57
|
+
"""
|
|
25
58
|
path = f"/repositories/{self._repository_id}"
|
|
26
59
|
params = {"query": sparql_query, "infer": str(infer).lower()}
|
|
27
|
-
headers = {"Accept": accept
|
|
60
|
+
headers = {"Accept": accept}
|
|
28
61
|
response = await self._client.get(path, params=params, headers=headers)
|
|
29
62
|
self._handle_repo_not_found_exception(response)
|
|
30
63
|
if "json" in response.headers.get("Content-Type", ""):
|
|
@@ -32,43 +65,78 @@ class AsyncRdf4JRepository:
|
|
|
32
65
|
return response.text
|
|
33
66
|
|
|
34
67
|
async def update(self, sparql_update: str):
|
|
68
|
+
"""Executes a SPARQL UPDATE command.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
sparql_update (str): The SPARQL update string.
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
RepositoryNotFoundException: If the repository doesn't exist.
|
|
75
|
+
httpx.HTTPStatusError: If the update fails.
|
|
76
|
+
"""
|
|
35
77
|
path = f"/repositories/{self._repository_id}/statements"
|
|
36
78
|
headers = {"Content-Type": Rdf4jContentType.SPARQL_UPDATE.value}
|
|
37
79
|
response = await self._client.post(path, data=sparql_update, headers=headers)
|
|
38
80
|
self._handle_repo_not_found_exception(response)
|
|
39
81
|
response.raise_for_status()
|
|
40
82
|
|
|
41
|
-
async def replace_statements(
|
|
42
|
-
self, rdf_data: str, content_type: Rdf4jContentType = Rdf4jContentType.TURTLE
|
|
43
|
-
):
|
|
44
|
-
path = f"/repositories/{self._repository_id}/statements"
|
|
45
|
-
headers = {"Content-Type": content_type.value}
|
|
46
|
-
response = await self._client.put(path, data=rdf_data, headers=headers)
|
|
47
|
-
self._handle_repo_not_found_exception(response)
|
|
48
|
-
response.raise_for_status()
|
|
49
|
-
|
|
50
83
|
async def get_namespaces(self):
|
|
84
|
+
"""Retrieves all namespaces in the repository.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
list[Namespace]: A list of namespace objects.
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
RepositoryNotFoundException: If the repository doesn't exist.
|
|
91
|
+
"""
|
|
51
92
|
path = f"/repositories/{self._repository_id}/namespaces"
|
|
52
93
|
headers = {"Accept": Rdf4jContentType.SPARQL_RESULTS_JSON}
|
|
53
94
|
response = await self._client.get(path, headers=headers)
|
|
95
|
+
self._handle_repo_not_found_exception(response)
|
|
54
96
|
|
|
55
|
-
|
|
56
|
-
response, format=
|
|
97
|
+
query_solutions = og.parse_query_results(
|
|
98
|
+
response.text, format=og.QueryResultsFormat.JSON
|
|
57
99
|
)
|
|
58
|
-
|
|
59
|
-
|
|
100
|
+
return [
|
|
101
|
+
Namespace.from_sparql_query_solution(query_solution)
|
|
102
|
+
for query_solution in query_solutions
|
|
103
|
+
]
|
|
60
104
|
|
|
61
|
-
async def set_namespace(self, prefix: str, namespace:
|
|
105
|
+
async def set_namespace(self, prefix: str, namespace: IRI):
|
|
106
|
+
"""Sets a namespace prefix.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
prefix (str): The namespace prefix.
|
|
110
|
+
namespace (IRI): The namespace URI.
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
RepositoryNotFoundException: If the repository doesn't exist.
|
|
114
|
+
NamespaceException: If the request fails.
|
|
115
|
+
"""
|
|
62
116
|
path = f"/repositories/{self._repository_id}/namespaces/{prefix}"
|
|
63
|
-
headers = {"Content-Type": Rdf4jContentType.NTRIPLES
|
|
64
|
-
response = await self._client.put(
|
|
117
|
+
headers = {"Content-Type": Rdf4jContentType.NTRIPLES}
|
|
118
|
+
response = await self._client.put(
|
|
119
|
+
path, content=namespace.value, headers=headers
|
|
120
|
+
)
|
|
65
121
|
self._handle_repo_not_found_exception(response)
|
|
66
122
|
if response.status_code != httpx.codes.NO_CONTENT:
|
|
67
123
|
raise NamespaceException(f"Failed to set namespace: {response.text}")
|
|
68
124
|
|
|
69
125
|
async def get_namespace(self, prefix: str) -> Namespace:
|
|
126
|
+
"""Gets a namespace by its prefix.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
prefix (str): The namespace prefix.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Namespace: The namespace object.
|
|
133
|
+
|
|
134
|
+
Raises:
|
|
135
|
+
RepositoryNotFoundException: If the repository doesn't exist.
|
|
136
|
+
NamespaceException: If retrieval fails.
|
|
137
|
+
"""
|
|
70
138
|
path = f"/repositories/{self._repository_id}/namespaces/{prefix}"
|
|
71
|
-
headers = {"Accept": Rdf4jContentType.NTRIPLES
|
|
139
|
+
headers = {"Accept": Rdf4jContentType.NTRIPLES}
|
|
72
140
|
response = await self._client.get(path, headers=headers)
|
|
73
141
|
self._handle_repo_not_found_exception(response)
|
|
74
142
|
|
|
@@ -78,12 +146,42 @@ class AsyncRdf4JRepository:
|
|
|
78
146
|
return Namespace(prefix, response.text)
|
|
79
147
|
|
|
80
148
|
async def delete_namespace(self, prefix: str):
|
|
149
|
+
"""Deletes a namespace by prefix.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
prefix (str): The namespace prefix.
|
|
153
|
+
|
|
154
|
+
Raises:
|
|
155
|
+
RepositoryNotFoundException: If the repository doesn't exist.
|
|
156
|
+
httpx.HTTPStatusError: If deletion fails.
|
|
157
|
+
"""
|
|
81
158
|
path = f"/repositories/{self._repository_id}/namespaces/{prefix}"
|
|
82
159
|
response = await self._client.delete(path)
|
|
83
160
|
self._handle_repo_not_found_exception(response)
|
|
84
161
|
response.raise_for_status()
|
|
85
162
|
|
|
163
|
+
async def clear_all_namespaces(self):
|
|
164
|
+
"""Removes all namespaces from the repository.
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
RepositoryNotFoundException: If the repository doesn't exist.
|
|
168
|
+
httpx.HTTPStatusError: If clearing fails.
|
|
169
|
+
"""
|
|
170
|
+
path = f"/repositories/{self._repository_id}/namespaces"
|
|
171
|
+
response = await self._client.delete(path)
|
|
172
|
+
self._handle_repo_not_found_exception(response)
|
|
173
|
+
response.raise_for_status()
|
|
174
|
+
|
|
86
175
|
async def size(self) -> int:
|
|
176
|
+
"""Gets the number of statements in the repository.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
int: The total number of RDF statements.
|
|
180
|
+
|
|
181
|
+
Raises:
|
|
182
|
+
RepositoryNotFoundException: If the repository doesn't exist.
|
|
183
|
+
RepositoryInternalException: If retrieval fails.
|
|
184
|
+
"""
|
|
87
185
|
path = f"/repositories/{self._repository_id}/size"
|
|
88
186
|
response = await self._client.get(path)
|
|
89
187
|
self._handle_repo_not_found_exception(response)
|
|
@@ -93,17 +191,201 @@ class AsyncRdf4JRepository:
|
|
|
93
191
|
|
|
94
192
|
return int(response.text.strip())
|
|
95
193
|
|
|
96
|
-
async def
|
|
194
|
+
async def get_statements(
|
|
195
|
+
self,
|
|
196
|
+
subject: Optional[Subject] = None,
|
|
197
|
+
predicate: Optional[Predicate] = None,
|
|
198
|
+
object_: Optional[Object] = None,
|
|
199
|
+
contexts: Optional[list[Context]] = None,
|
|
200
|
+
infer: bool = True,
|
|
201
|
+
) -> QuadResultSet:
|
|
202
|
+
"""Retrieves statements matching the given pattern.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
subject (Optional[Subject]): Filter by subject.
|
|
206
|
+
predicate (Optional[Predicate]): Filter by predicate.
|
|
207
|
+
object_ (Optional[Object]): Filter by object.
|
|
208
|
+
contexts (Optional[list[Context]]): Filter by context (named graph).
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
QuadResultSet: QuadResultSet of matching RDF statements.
|
|
212
|
+
|
|
213
|
+
Raises:
|
|
214
|
+
RepositoryNotFoundException: If the repository doesn't exist.
|
|
215
|
+
"""
|
|
216
|
+
path = f"/repositories/{self._repository_id}/statements"
|
|
217
|
+
params = {}
|
|
218
|
+
|
|
219
|
+
if subject:
|
|
220
|
+
params["subj"] = str(subject)
|
|
221
|
+
if predicate:
|
|
222
|
+
params["pred"] = str(predicate)
|
|
223
|
+
if object_:
|
|
224
|
+
params["obj"] = str(object_)
|
|
225
|
+
if contexts:
|
|
226
|
+
params["context"] = [str(ctx) for ctx in contexts]
|
|
227
|
+
params["infer"] = str(infer).lower()
|
|
228
|
+
|
|
229
|
+
headers = {"Accept": Rdf4jContentType.NQUADS}
|
|
230
|
+
response = await self._client.get(path, params=params, headers=headers)
|
|
231
|
+
return og.parse(response.content, format=og.RdfFormat.N_QUADS)
|
|
232
|
+
|
|
233
|
+
async def delete_statements(
|
|
234
|
+
self,
|
|
235
|
+
subject: Optional[Subject] = None,
|
|
236
|
+
predicate: Optional[Predicate] = None,
|
|
237
|
+
object_: Optional[Object] = None,
|
|
238
|
+
contexts: Optional[list[Context]] = None,
|
|
239
|
+
):
|
|
240
|
+
"""Deletes statements from the repository matching the given pattern.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
subject (Optional[Subject]): Filter by subject (N-Triples encoded).
|
|
244
|
+
predicate (Optional[Predicate]): Filter by predicate (N-Triples encoded).
|
|
245
|
+
object_ (Optional[Object]): Filter by object (N-Triples encoded).
|
|
246
|
+
contexts (Optional[list[Context]]): One or more specific contexts to restrict deletion to.
|
|
247
|
+
Use 'null' as a string to delete context-less statements.
|
|
248
|
+
|
|
249
|
+
Raises:
|
|
250
|
+
RepositoryNotFoundException: If the repository does not exist.
|
|
251
|
+
RepositoryUpdateException: If the deletion fails.
|
|
252
|
+
"""
|
|
97
253
|
path = f"/repositories/{self._repository_id}/statements"
|
|
98
|
-
|
|
254
|
+
params = {}
|
|
255
|
+
|
|
256
|
+
if subject:
|
|
257
|
+
params["subj"] = str(subject)
|
|
258
|
+
if predicate:
|
|
259
|
+
params["pred"] = str(predicate)
|
|
260
|
+
if object_:
|
|
261
|
+
params["obj"] = str(object_)
|
|
262
|
+
if contexts:
|
|
263
|
+
params["context"] = [str(ctx) for ctx in contexts]
|
|
264
|
+
|
|
265
|
+
response = await self._client.delete(path, params=params)
|
|
266
|
+
self._handle_repo_not_found_exception(response)
|
|
267
|
+
|
|
268
|
+
if response.status_code != httpx.codes.NO_CONTENT:
|
|
269
|
+
raise RepositoryUpdateException(
|
|
270
|
+
f"Failed to delete statements: {response.text}"
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
async def add_statement(
|
|
274
|
+
self,
|
|
275
|
+
subject: Subject,
|
|
276
|
+
predicate: Predicate,
|
|
277
|
+
object: Object,
|
|
278
|
+
context: Optional[Context] = None,
|
|
279
|
+
):
|
|
280
|
+
"""Adds a single RDF statement to the repository.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
subject (Node): The subject of the triple.
|
|
284
|
+
predicate (Node): The predicate of the triple.
|
|
285
|
+
object (Node): The object of the triple.
|
|
286
|
+
context (IdentifiedNode): The context (named graph).
|
|
287
|
+
|
|
288
|
+
Raises:
|
|
289
|
+
RepositoryNotFoundException: If the repository doesn't exist.
|
|
290
|
+
httpx.HTTPStatusError: If addition fails.
|
|
291
|
+
"""
|
|
292
|
+
path = f"/repositories/{self._repository_id}/statements"
|
|
293
|
+
statement: Triple | Quad
|
|
294
|
+
if context is None:
|
|
295
|
+
statement = Triple(subject, predicate, object)
|
|
296
|
+
else:
|
|
297
|
+
statement = Quad(subject, predicate, object, context)
|
|
298
|
+
|
|
99
299
|
response = await self._client.post(
|
|
100
|
-
path,
|
|
300
|
+
path,
|
|
301
|
+
content=serialize_statements([statement]),
|
|
302
|
+
headers={"Content-Type": Rdf4jContentType.NQUADS},
|
|
101
303
|
)
|
|
102
304
|
self._handle_repo_not_found_exception(response)
|
|
103
|
-
response.
|
|
305
|
+
if response.status_code != httpx.codes.NO_CONTENT:
|
|
306
|
+
raise RepositoryUpdateException(f"Failed to add statement: {response.text}")
|
|
307
|
+
|
|
308
|
+
async def add_statements(self, statements: Iterable[Quad] | Iterable[Triple]):
|
|
309
|
+
"""Adds a list of RDF statements to the repository.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
statements (Iterable[Quad] | Iterable[Triple]): A list of RDF statements.
|
|
313
|
+
|
|
314
|
+
Raises:
|
|
315
|
+
RepositoryNotFoundException: If the repository doesn't exist.
|
|
316
|
+
httpx.HTTPStatusError: If addition fails.
|
|
317
|
+
"""
|
|
318
|
+
path = f"/repositories/{self._repository_id}/statements"
|
|
319
|
+
response = await self._client.post(
|
|
320
|
+
path,
|
|
321
|
+
content=serialize_statements(statements),
|
|
322
|
+
headers={"Content-Type": Rdf4jContentType.NQUADS},
|
|
323
|
+
)
|
|
324
|
+
self._handle_repo_not_found_exception(response)
|
|
325
|
+
if response.status_code != httpx.codes.NO_CONTENT:
|
|
326
|
+
raise RepositoryUpdateException(
|
|
327
|
+
f"Failed to add statements: {response.text}"
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
async def replace_statements(
|
|
331
|
+
self,
|
|
332
|
+
statements: Iterable[Quad] | Iterable[Triple],
|
|
333
|
+
contexts: Optional[Iterable[Context]] = None,
|
|
334
|
+
base_uri: Optional[str] = None,
|
|
335
|
+
):
|
|
336
|
+
"""Replaces all repository statements with the given RDF data.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
statements (Iterable[Quad] | Iterable[Triple]): RDF statements to load.
|
|
340
|
+
contexts (Optional[Iterable[Context]]): One or more specific contexts to restrict deletion to.
|
|
341
|
+
|
|
342
|
+
Raises:
|
|
343
|
+
RepositoryNotFoundException: If the repository doesn't exist.
|
|
344
|
+
httpx.HTTPStatusError: If the operation fails.
|
|
345
|
+
"""
|
|
346
|
+
path = f"/repositories/{self._repository_id}/statements"
|
|
347
|
+
headers = {"Content-Type": Rdf4jContentType.NQUADS}
|
|
348
|
+
|
|
349
|
+
params = {}
|
|
350
|
+
if contexts:
|
|
351
|
+
params["context"] = [str(ctx) for ctx in contexts]
|
|
352
|
+
if base_uri:
|
|
353
|
+
params["baseUri"] = base_uri
|
|
354
|
+
|
|
355
|
+
response = await self._client.put(
|
|
356
|
+
path,
|
|
357
|
+
content=serialize_statements(statements),
|
|
358
|
+
headers=headers,
|
|
359
|
+
params=params,
|
|
360
|
+
)
|
|
361
|
+
self._handle_repo_not_found_exception(response)
|
|
362
|
+
if response.status_code != httpx.codes.NO_CONTENT:
|
|
363
|
+
raise RepositoryUpdateException(
|
|
364
|
+
f"Failed to replace statements: {response.text}"
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
async def get_named_graph(self, graph: str) -> AsyncNamedGraph:
|
|
368
|
+
"""Retrieves a named graph in the repository.
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
AsyncNamedGraph: A named graph object.
|
|
372
|
+
"""
|
|
373
|
+
return AsyncNamedGraph(self._client, self._repository_id, graph)
|
|
104
374
|
|
|
105
375
|
def _handle_repo_not_found_exception(self, response: httpx.Response):
|
|
376
|
+
"""Raises a RepositoryNotFoundException if response is 404.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
response (httpx.Response): HTTP response object.
|
|
380
|
+
|
|
381
|
+
Raises:
|
|
382
|
+
RepositoryNotFoundException: If repository is not found.
|
|
383
|
+
"""
|
|
106
384
|
if response.status_code == httpx.codes.NOT_FOUND:
|
|
107
385
|
raise RepositoryNotFoundException(
|
|
108
386
|
f"Repository {self._repository_id} not found"
|
|
109
387
|
)
|
|
388
|
+
|
|
389
|
+
@property
|
|
390
|
+
def repository_id(self) -> str:
|
|
391
|
+
return self._repository_id
|
|
@@ -1,13 +1,34 @@
|
|
|
1
|
-
class RepositoryCreationException(Exception):
|
|
1
|
+
class RepositoryCreationException(Exception):
|
|
2
|
+
"""
|
|
3
|
+
Exception raised when a repository creation fails.
|
|
4
|
+
"""
|
|
2
5
|
|
|
3
6
|
|
|
4
|
-
class RepositoryDeletionException(Exception):
|
|
7
|
+
class RepositoryDeletionException(Exception):
|
|
8
|
+
"""
|
|
9
|
+
Exception raised when a repository deletion fails.
|
|
10
|
+
"""
|
|
5
11
|
|
|
6
12
|
|
|
7
|
-
class NamespaceException(Exception):
|
|
13
|
+
class NamespaceException(Exception):
|
|
14
|
+
"""
|
|
15
|
+
Exception raised when a namespace operation fails.
|
|
16
|
+
"""
|
|
8
17
|
|
|
9
18
|
|
|
10
|
-
class RepositoryNotFoundException(Exception):
|
|
19
|
+
class RepositoryNotFoundException(Exception):
|
|
20
|
+
"""
|
|
21
|
+
Exception raised when a repository is not found.
|
|
22
|
+
"""
|
|
11
23
|
|
|
12
24
|
|
|
13
|
-
class RepositoryInternalException(Exception):
|
|
25
|
+
class RepositoryInternalException(Exception):
|
|
26
|
+
"""
|
|
27
|
+
Exception raised when a repository internal error occurs.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RepositoryUpdateException(Exception):
|
|
32
|
+
"""
|
|
33
|
+
Exception raised when a repository update fails.
|
|
34
|
+
"""
|
rdf4j_python/model/__init__.py
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
)
|
|
1
|
+
"""
|
|
2
|
+
RDF4J Python Model Module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from ._namespace import Namespace
|
|
7
6
|
from ._repository_info import RepositoryMetadata
|
|
8
7
|
|
|
9
8
|
__all__ = [
|
|
10
|
-
"IRI",
|
|
11
9
|
"Namespace",
|
|
12
|
-
"RepositoryConfig",
|
|
13
|
-
"MemoryStoreConfig",
|
|
14
|
-
"NativeStoreConfig",
|
|
15
10
|
"RepositoryMetadata",
|
|
16
11
|
]
|
rdf4j_python/model/_namespace.py
CHANGED
|
@@ -1,56 +1,157 @@
|
|
|
1
|
-
|
|
1
|
+
import pyoxigraph as og
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from rdflib.namespace import Namespace as RdflibNamespace
|
|
5
|
-
from rdflib.term import Identifier, Variable
|
|
3
|
+
from rdf4j_python.model.term import IRI
|
|
6
4
|
|
|
7
|
-
from ._base_model import _BaseModel
|
|
8
5
|
|
|
6
|
+
class _Namespace(str):
|
|
7
|
+
def __new__(cls, value: str | bytes) -> "Namespace":
|
|
8
|
+
try:
|
|
9
|
+
rt = str.__new__(cls, value)
|
|
10
|
+
except UnicodeDecodeError:
|
|
11
|
+
rt = str.__new__(cls, value, "utf-8")
|
|
12
|
+
return rt
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
def term(self, name: str) -> IRI:
|
|
15
|
+
return IRI(self + (name if isinstance(name, str) else ""))
|
|
16
|
+
|
|
17
|
+
def __getitem__(self, key: str) -> IRI:
|
|
18
|
+
return self.term(key)
|
|
19
|
+
|
|
20
|
+
def __getattr__(self, name: str) -> IRI:
|
|
21
|
+
if name.startswith("__"):
|
|
22
|
+
raise AttributeError
|
|
23
|
+
return self.term(name)
|
|
24
|
+
|
|
25
|
+
def __repr__(self) -> str:
|
|
26
|
+
return f"Namespace({super().__repr__()})"
|
|
27
|
+
|
|
28
|
+
def __contains__(self, ref: str) -> bool:
|
|
29
|
+
return ref.startswith(self)
|
|
11
30
|
|
|
12
31
|
|
|
13
32
|
class Namespace:
|
|
33
|
+
"""
|
|
34
|
+
Represents a namespace in RDF4J.
|
|
35
|
+
"""
|
|
36
|
+
|
|
14
37
|
_prefix: str
|
|
15
|
-
_namespace:
|
|
38
|
+
_namespace: _Namespace
|
|
16
39
|
|
|
17
40
|
def __init__(self, prefix: str, namespace: str):
|
|
41
|
+
"""
|
|
42
|
+
Initializes a new Namespace.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
prefix (str): The prefix of the namespace.
|
|
46
|
+
namespace (str): The namespace URI.
|
|
47
|
+
"""
|
|
18
48
|
self._prefix = prefix
|
|
19
|
-
self._namespace =
|
|
49
|
+
self._namespace = _Namespace(namespace)
|
|
20
50
|
|
|
21
51
|
@classmethod
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
|
|
52
|
+
def from_sparql_query_solution(
|
|
53
|
+
cls, query_solution: og.QuerySolution
|
|
54
|
+
) -> "Namespace":
|
|
55
|
+
"""
|
|
56
|
+
Creates a Namespace from a binding.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
binding (Mapping[Variable, Identifier]): The binding.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Namespace: The created Namespace.
|
|
63
|
+
"""
|
|
64
|
+
prefix: og.Literal = query_solution[og.Variable("prefix")]
|
|
65
|
+
namespace: og.NamedNode = query_solution[og.Variable("namespace")]
|
|
25
66
|
return cls(
|
|
26
|
-
prefix=prefix,
|
|
27
|
-
namespace=namespace,
|
|
67
|
+
prefix=prefix.value,
|
|
68
|
+
namespace=namespace.value,
|
|
28
69
|
)
|
|
29
70
|
|
|
30
71
|
def __str__(self):
|
|
72
|
+
"""
|
|
73
|
+
Returns a string representation of the Namespace.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
str: A string representation of the Namespace.
|
|
77
|
+
"""
|
|
31
78
|
return f"{self._prefix}: {self._namespace}"
|
|
32
79
|
|
|
33
80
|
def __repr__(self):
|
|
81
|
+
"""
|
|
82
|
+
Returns a string representation of the Namespace.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
str: A string representation of the Namespace.
|
|
86
|
+
"""
|
|
34
87
|
return f"Namespace(prefix={self._prefix}, namespace={self._namespace})"
|
|
35
88
|
|
|
36
89
|
def __contains__(self, item: str) -> bool:
|
|
90
|
+
"""
|
|
91
|
+
Checks if the Namespace contains a given item.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
item (str): The item to check.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
bool: True if the Namespace contains the item, False otherwise.
|
|
98
|
+
"""
|
|
37
99
|
return item in self._namespace
|
|
38
100
|
|
|
39
101
|
def term(self, name: str) -> IRI:
|
|
40
|
-
|
|
102
|
+
"""
|
|
103
|
+
Returns the IRI for a given term.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
name (str): The term name.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
IRI: The IRI for the term.
|
|
110
|
+
"""
|
|
111
|
+
return self._namespace.term(name)
|
|
41
112
|
|
|
42
113
|
def __getitem__(self, item: str) -> IRI:
|
|
114
|
+
"""
|
|
115
|
+
Returns the IRI for a given term.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
item (str): The term name.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
IRI: The IRI for the term.
|
|
122
|
+
"""
|
|
43
123
|
return self.term(item)
|
|
44
124
|
|
|
45
125
|
def __getattr__(self, item: str) -> IRI:
|
|
126
|
+
"""
|
|
127
|
+
Returns the IRI for a given term.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
item (str): The term name.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
IRI: The IRI for the term.
|
|
134
|
+
"""
|
|
46
135
|
if item.startswith("__"):
|
|
47
136
|
raise AttributeError
|
|
48
137
|
return self.term(item)
|
|
49
138
|
|
|
50
139
|
@property
|
|
51
140
|
def namespace(self) -> IRI:
|
|
141
|
+
"""
|
|
142
|
+
Returns the namespace URI.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
IRI: The namespace URI.
|
|
146
|
+
"""
|
|
52
147
|
return IRI(self._namespace)
|
|
53
148
|
|
|
54
149
|
@property
|
|
55
150
|
def prefix(self) -> str:
|
|
151
|
+
"""
|
|
152
|
+
Returns the prefix of the namespace.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
str: The prefix of the namespace.
|
|
156
|
+
"""
|
|
56
157
|
return self._prefix
|