rdf4j-python 0.1.2__tar.gz → 0.1.3__tar.gz
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-0.1.2 → rdf4j_python-0.1.3}/PKG-INFO +2 -2
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/pyproject.toml +3 -6
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/_driver/_async_named_graph.py +8 -7
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/_driver/_async_rdf4j_db.py +5 -5
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/_driver/_async_repository.py +48 -39
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/model/__init__.py +0 -2
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/model/_namespace.py +38 -15
- rdf4j_python-0.1.3/rdf4j_python/model/_repository_info.py +62 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/model/repository_config.py +106 -53
- rdf4j_python-0.1.3/rdf4j_python/model/term.py +20 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/model/vocabulary.py +1 -0
- rdf4j_python-0.1.3/rdf4j_python/utils/helpers.py +20 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python.egg-info/PKG-INFO +2 -2
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python.egg-info/SOURCES.txt +1 -2
- rdf4j_python-0.1.3/rdf4j_python.egg-info/requires.txt +2 -0
- rdf4j_python-0.1.3/tests/test_async_named_graph.py +89 -0
- rdf4j_python-0.1.3/tests/test_helpers.py +21 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/tests/test_rdf4j_repository.py +83 -65
- rdf4j_python-0.1.2/rdf4j_python/model/_base_model.py +0 -48
- rdf4j_python-0.1.2/rdf4j_python/model/_dataset.py +0 -38
- rdf4j_python-0.1.2/rdf4j_python/model/_repository_info.py +0 -52
- rdf4j_python-0.1.2/rdf4j_python/model/term.py +0 -14
- rdf4j_python-0.1.2/rdf4j_python/utils/helpers.py +0 -22
- rdf4j_python-0.1.2/rdf4j_python.egg-info/requires.txt +0 -2
- rdf4j_python-0.1.2/tests/test_async_named_graph.py +0 -83
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/LICENSE +0 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/README.md +0 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/__init__.py +0 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/_client/__init__.py +0 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/_client/_client.py +0 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/_driver/__init__.py +0 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/exception/__init__.py +0 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/exception/repo_exception.py +0 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/utils/__init__.py +0 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python/utils/const.py +0 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python.egg-info/dependency_links.txt +0 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/rdf4j_python.egg-info/top_level.txt +0 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/setup.cfg +0 -0
- {rdf4j_python-0.1.2 → rdf4j_python-0.1.3}/tests/test_client.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rdf4j-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: The Python client for RDF4J
|
|
5
5
|
Author-email: Chengxu Bian <cbian564@gmail.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Requires-Dist: httpx>=0.28.1
|
|
10
|
-
Requires-Dist:
|
|
10
|
+
Requires-Dist: pyoxigraph>=0.4.10
|
|
11
11
|
Dynamic: license-file
|
|
12
12
|
|
|
13
13
|
# 🐍 rdf4j-python
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "rdf4j-python"
|
|
3
3
|
authors = [{ name = "Chengxu Bian", email = "cbian564@gmail.com" }]
|
|
4
|
-
version = "0.1.
|
|
4
|
+
version = "0.1.3"
|
|
5
5
|
description = "The Python client for RDF4J"
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
requires-python = ">=3.10"
|
|
8
|
-
dependencies = ["httpx>=0.28.1", "
|
|
8
|
+
dependencies = ["httpx>=0.28.1", "pyoxigraph>=0.4.10"]
|
|
9
9
|
|
|
10
10
|
[dependency-groups]
|
|
11
11
|
dev = [
|
|
@@ -14,10 +14,7 @@ dev = [
|
|
|
14
14
|
"pytest-docker>=3.2.1",
|
|
15
15
|
"ruff>=0.11.8",
|
|
16
16
|
]
|
|
17
|
-
docs = [
|
|
18
|
-
"furo>=2024.8.6",
|
|
19
|
-
"sphinx>=8",
|
|
20
|
-
]
|
|
17
|
+
docs = ["furo>=2024.8.6", "sphinx>=8"]
|
|
21
18
|
|
|
22
19
|
|
|
23
20
|
[tool.pytest.ini_options]
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from typing import Iterable
|
|
2
2
|
|
|
3
|
+
import pyoxigraph as og
|
|
4
|
+
|
|
3
5
|
from rdf4j_python._client import AsyncApiClient
|
|
4
|
-
from rdf4j_python.model import
|
|
5
|
-
from rdf4j_python.model.term import IRI, RDFStatement
|
|
6
|
+
from rdf4j_python.model.term import IRI, Quad, QuadResultSet, Triple
|
|
6
7
|
from rdf4j_python.utils.const import Rdf4jContentType
|
|
7
8
|
from rdf4j_python.utils.helpers import serialize_statements
|
|
8
9
|
|
|
@@ -22,11 +23,11 @@ class AsyncNamedGraph:
|
|
|
22
23
|
self._repository_id = repository_id
|
|
23
24
|
self._graph_uri = graph_uri
|
|
24
25
|
|
|
25
|
-
async def get(self) ->
|
|
26
|
+
async def get(self) -> QuadResultSet:
|
|
26
27
|
"""Fetches all RDF statements from this named graph.
|
|
27
28
|
|
|
28
29
|
Returns:
|
|
29
|
-
|
|
30
|
+
QuadResultSet: RDF data serialized in the requested format.
|
|
30
31
|
|
|
31
32
|
Raises:
|
|
32
33
|
httpx.HTTPStatusError: If the request fails.
|
|
@@ -35,13 +36,13 @@ class AsyncNamedGraph:
|
|
|
35
36
|
headers = {"Accept": Rdf4jContentType.NQUADS}
|
|
36
37
|
response = await self._client.get(path, headers=headers)
|
|
37
38
|
response.raise_for_status()
|
|
38
|
-
return
|
|
39
|
+
return og.parse(response.content, format=og.RdfFormat.N_QUADS)
|
|
39
40
|
|
|
40
|
-
async def add(self, statements: Iterable[
|
|
41
|
+
async def add(self, statements: Iterable[Quad] | Iterable[Triple]):
|
|
41
42
|
"""Adds RDF statements to this named graph.
|
|
42
43
|
|
|
43
44
|
Args:
|
|
44
|
-
statements (Iterable[
|
|
45
|
+
statements (Iterable[Quad] | Iterable[Triple]): RDF statements to add.
|
|
45
46
|
|
|
46
47
|
Raises:
|
|
47
48
|
httpx.HTTPStatusError: If the request fails.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import httpx
|
|
2
|
-
import
|
|
2
|
+
import pyoxigraph as og
|
|
3
3
|
|
|
4
4
|
from rdf4j_python._client import AsyncApiClient
|
|
5
5
|
from rdf4j_python.exception.repo_exception import (
|
|
@@ -66,12 +66,12 @@ class AsyncRdf4j:
|
|
|
66
66
|
"/repositories",
|
|
67
67
|
headers={"Accept": Rdf4jContentType.SPARQL_RESULTS_JSON},
|
|
68
68
|
)
|
|
69
|
-
|
|
70
|
-
response, format=
|
|
69
|
+
query_solutions = og.parse_query_results(
|
|
70
|
+
response.text, format=og.QueryResultsFormat.JSON
|
|
71
71
|
)
|
|
72
72
|
return [
|
|
73
|
-
RepositoryMetadata.
|
|
74
|
-
for
|
|
73
|
+
RepositoryMetadata.from_sparql_query_solution(query_solution)
|
|
74
|
+
for query_solution in query_solutions
|
|
75
75
|
]
|
|
76
76
|
|
|
77
77
|
async def get_repository(self, repository_id: str) -> AsyncRdf4JRepository:
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
from typing import Iterable, Optional
|
|
2
2
|
|
|
3
3
|
import httpx
|
|
4
|
-
import
|
|
5
|
-
import rdflib.resource
|
|
6
|
-
import rdflib.serializer
|
|
7
|
-
import rdflib.store
|
|
4
|
+
import pyoxigraph as og
|
|
8
5
|
|
|
9
6
|
from rdf4j_python._client import AsyncApiClient
|
|
10
7
|
from rdf4j_python._driver._async_named_graph import AsyncNamedGraph
|
|
@@ -14,13 +11,16 @@ from rdf4j_python.exception.repo_exception import (
|
|
|
14
11
|
RepositoryNotFoundException,
|
|
15
12
|
RepositoryUpdateException,
|
|
16
13
|
)
|
|
17
|
-
from rdf4j_python.model import Namespace
|
|
14
|
+
from rdf4j_python.model import Namespace
|
|
18
15
|
from rdf4j_python.model.term import (
|
|
16
|
+
IRI,
|
|
19
17
|
Context,
|
|
20
18
|
Object,
|
|
21
19
|
Predicate,
|
|
22
|
-
|
|
20
|
+
Quad,
|
|
21
|
+
QuadResultSet,
|
|
23
22
|
Subject,
|
|
23
|
+
Triple,
|
|
24
24
|
)
|
|
25
25
|
from rdf4j_python.utils.const import Rdf4jContentType
|
|
26
26
|
from rdf4j_python.utils.helpers import serialize_statements
|
|
@@ -57,7 +57,7 @@ class AsyncRdf4JRepository:
|
|
|
57
57
|
"""
|
|
58
58
|
path = f"/repositories/{self._repository_id}"
|
|
59
59
|
params = {"query": sparql_query, "infer": str(infer).lower()}
|
|
60
|
-
headers = {"Accept": accept
|
|
60
|
+
headers = {"Accept": accept}
|
|
61
61
|
response = await self._client.get(path, params=params, headers=headers)
|
|
62
62
|
self._handle_repo_not_found_exception(response)
|
|
63
63
|
if "json" in response.headers.get("Content-Type", ""):
|
|
@@ -92,26 +92,32 @@ class AsyncRdf4JRepository:
|
|
|
92
92
|
path = f"/repositories/{self._repository_id}/namespaces"
|
|
93
93
|
headers = {"Accept": Rdf4jContentType.SPARQL_RESULTS_JSON}
|
|
94
94
|
response = await self._client.get(path, headers=headers)
|
|
95
|
-
result = rdflib.query.Result.parse(
|
|
96
|
-
response, format=Rdf4jContentType.SPARQL_RESULTS_JSON
|
|
97
|
-
)
|
|
98
95
|
self._handle_repo_not_found_exception(response)
|
|
99
|
-
return [Namespace.from_rdflib_binding(binding) for binding in result.bindings]
|
|
100
96
|
|
|
101
|
-
|
|
97
|
+
query_solutions = og.parse_query_results(
|
|
98
|
+
response.text, format=og.QueryResultsFormat.JSON
|
|
99
|
+
)
|
|
100
|
+
return [
|
|
101
|
+
Namespace.from_sparql_query_solution(query_solution)
|
|
102
|
+
for query_solution in query_solutions
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
async def set_namespace(self, prefix: str, namespace: IRI):
|
|
102
106
|
"""Sets a namespace prefix.
|
|
103
107
|
|
|
104
108
|
Args:
|
|
105
109
|
prefix (str): The namespace prefix.
|
|
106
|
-
namespace (
|
|
110
|
+
namespace (IRI): The namespace URI.
|
|
107
111
|
|
|
108
112
|
Raises:
|
|
109
113
|
RepositoryNotFoundException: If the repository doesn't exist.
|
|
110
114
|
NamespaceException: If the request fails.
|
|
111
115
|
"""
|
|
112
116
|
path = f"/repositories/{self._repository_id}/namespaces/{prefix}"
|
|
113
|
-
headers = {"Content-Type": Rdf4jContentType.NTRIPLES
|
|
114
|
-
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
|
+
)
|
|
115
121
|
self._handle_repo_not_found_exception(response)
|
|
116
122
|
if response.status_code != httpx.codes.NO_CONTENT:
|
|
117
123
|
raise NamespaceException(f"Failed to set namespace: {response.text}")
|
|
@@ -130,7 +136,7 @@ class AsyncRdf4JRepository:
|
|
|
130
136
|
NamespaceException: If retrieval fails.
|
|
131
137
|
"""
|
|
132
138
|
path = f"/repositories/{self._repository_id}/namespaces/{prefix}"
|
|
133
|
-
headers = {"Accept": Rdf4jContentType.NTRIPLES
|
|
139
|
+
headers = {"Accept": Rdf4jContentType.NTRIPLES}
|
|
134
140
|
response = await self._client.get(path, headers=headers)
|
|
135
141
|
self._handle_repo_not_found_exception(response)
|
|
136
142
|
|
|
@@ -192,7 +198,7 @@ class AsyncRdf4JRepository:
|
|
|
192
198
|
object_: Optional[Object] = None,
|
|
193
199
|
contexts: Optional[list[Context]] = None,
|
|
194
200
|
infer: bool = True,
|
|
195
|
-
) ->
|
|
201
|
+
) -> QuadResultSet:
|
|
196
202
|
"""Retrieves statements matching the given pattern.
|
|
197
203
|
|
|
198
204
|
Args:
|
|
@@ -202,7 +208,7 @@ class AsyncRdf4JRepository:
|
|
|
202
208
|
contexts (Optional[list[Context]]): Filter by context (named graph).
|
|
203
209
|
|
|
204
210
|
Returns:
|
|
205
|
-
|
|
211
|
+
QuadResultSet: QuadResultSet of matching RDF statements.
|
|
206
212
|
|
|
207
213
|
Raises:
|
|
208
214
|
RepositoryNotFoundException: If the repository doesn't exist.
|
|
@@ -211,20 +217,18 @@ class AsyncRdf4JRepository:
|
|
|
211
217
|
params = {}
|
|
212
218
|
|
|
213
219
|
if subject:
|
|
214
|
-
params["subj"] = subject
|
|
220
|
+
params["subj"] = str(subject)
|
|
215
221
|
if predicate:
|
|
216
|
-
params["pred"] = predicate
|
|
222
|
+
params["pred"] = str(predicate)
|
|
217
223
|
if object_:
|
|
218
|
-
params["obj"] = object_
|
|
224
|
+
params["obj"] = str(object_)
|
|
219
225
|
if contexts:
|
|
220
|
-
params["context"] = [ctx
|
|
226
|
+
params["context"] = [str(ctx) for ctx in contexts]
|
|
221
227
|
params["infer"] = str(infer).lower()
|
|
222
228
|
|
|
223
229
|
headers = {"Accept": Rdf4jContentType.NQUADS}
|
|
224
230
|
response = await self._client.get(path, params=params, headers=headers)
|
|
225
|
-
|
|
226
|
-
dataset.parse(data=response.text, format="nquads")
|
|
227
|
-
return dataset
|
|
231
|
+
return og.parse(response.content, format=og.RdfFormat.N_QUADS)
|
|
228
232
|
|
|
229
233
|
async def delete_statements(
|
|
230
234
|
self,
|
|
@@ -250,13 +254,13 @@ class AsyncRdf4JRepository:
|
|
|
250
254
|
params = {}
|
|
251
255
|
|
|
252
256
|
if subject:
|
|
253
|
-
params["subj"] = subject
|
|
257
|
+
params["subj"] = str(subject)
|
|
254
258
|
if predicate:
|
|
255
|
-
params["pred"] = predicate
|
|
259
|
+
params["pred"] = str(predicate)
|
|
256
260
|
if object_:
|
|
257
|
-
params["obj"] = object_
|
|
261
|
+
params["obj"] = str(object_)
|
|
258
262
|
if contexts:
|
|
259
|
-
params["context"] = [ctx
|
|
263
|
+
params["context"] = [str(ctx) for ctx in contexts]
|
|
260
264
|
|
|
261
265
|
response = await self._client.delete(path, params=params)
|
|
262
266
|
self._handle_repo_not_found_exception(response)
|
|
@@ -286,21 +290,26 @@ class AsyncRdf4JRepository:
|
|
|
286
290
|
httpx.HTTPStatusError: If addition fails.
|
|
287
291
|
"""
|
|
288
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
|
+
|
|
289
299
|
response = await self._client.post(
|
|
290
300
|
path,
|
|
291
|
-
content=serialize_statements([
|
|
301
|
+
content=serialize_statements([statement]),
|
|
292
302
|
headers={"Content-Type": Rdf4jContentType.NQUADS},
|
|
293
303
|
)
|
|
294
304
|
self._handle_repo_not_found_exception(response)
|
|
295
305
|
if response.status_code != httpx.codes.NO_CONTENT:
|
|
296
306
|
raise RepositoryUpdateException(f"Failed to add statement: {response.text}")
|
|
297
307
|
|
|
298
|
-
async def add_statements(self, statements: Iterable[
|
|
308
|
+
async def add_statements(self, statements: Iterable[Quad] | Iterable[Triple]):
|
|
299
309
|
"""Adds a list of RDF statements to the repository.
|
|
300
310
|
|
|
301
311
|
Args:
|
|
302
|
-
statements (Iterable[
|
|
303
|
-
RDFStatement: A tuple of subject, predicate, object, and context.
|
|
312
|
+
statements (Iterable[Quad] | Iterable[Triple]): A list of RDF statements.
|
|
304
313
|
|
|
305
314
|
Raises:
|
|
306
315
|
RepositoryNotFoundException: If the repository doesn't exist.
|
|
@@ -320,26 +329,26 @@ class AsyncRdf4JRepository:
|
|
|
320
329
|
|
|
321
330
|
async def replace_statements(
|
|
322
331
|
self,
|
|
323
|
-
statements: Iterable[
|
|
324
|
-
contexts: Optional[
|
|
332
|
+
statements: Iterable[Quad] | Iterable[Triple],
|
|
333
|
+
contexts: Optional[Iterable[Context]] = None,
|
|
325
334
|
base_uri: Optional[str] = None,
|
|
326
335
|
):
|
|
327
336
|
"""Replaces all repository statements with the given RDF data.
|
|
328
337
|
|
|
329
338
|
Args:
|
|
330
|
-
statements (Iterable[
|
|
331
|
-
contexts (Optional[
|
|
339
|
+
statements (Iterable[Quad] | Iterable[Triple]): RDF statements to load.
|
|
340
|
+
contexts (Optional[Iterable[Context]]): One or more specific contexts to restrict deletion to.
|
|
332
341
|
|
|
333
342
|
Raises:
|
|
334
343
|
RepositoryNotFoundException: If the repository doesn't exist.
|
|
335
344
|
httpx.HTTPStatusError: If the operation fails.
|
|
336
345
|
"""
|
|
337
346
|
path = f"/repositories/{self._repository_id}/statements"
|
|
338
|
-
headers = {"Content-Type": Rdf4jContentType.NQUADS
|
|
347
|
+
headers = {"Content-Type": Rdf4jContentType.NQUADS}
|
|
339
348
|
|
|
340
349
|
params = {}
|
|
341
350
|
if contexts:
|
|
342
|
-
params["context"] = [ctx
|
|
351
|
+
params["context"] = [str(ctx) for ctx in contexts]
|
|
343
352
|
if base_uri:
|
|
344
353
|
params["baseUri"] = base_uri
|
|
345
354
|
|
|
@@ -1,11 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
from rdflib.namespace import Namespace as RdflibNamespace
|
|
4
|
-
from rdflib.term import Identifier, Variable
|
|
1
|
+
import pyoxigraph as og
|
|
5
2
|
|
|
6
3
|
from rdf4j_python.model.term import IRI
|
|
7
4
|
|
|
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
|
|
13
|
+
|
|
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)
|
|
9
30
|
|
|
10
31
|
|
|
11
32
|
class Namespace:
|
|
@@ -14,7 +35,7 @@ class Namespace:
|
|
|
14
35
|
"""
|
|
15
36
|
|
|
16
37
|
_prefix: str
|
|
17
|
-
_namespace:
|
|
38
|
+
_namespace: _Namespace
|
|
18
39
|
|
|
19
40
|
def __init__(self, prefix: str, namespace: str):
|
|
20
41
|
"""
|
|
@@ -25,24 +46,26 @@ class Namespace:
|
|
|
25
46
|
namespace (str): The namespace URI.
|
|
26
47
|
"""
|
|
27
48
|
self._prefix = prefix
|
|
28
|
-
self._namespace =
|
|
49
|
+
self._namespace = _Namespace(namespace)
|
|
29
50
|
|
|
30
51
|
@classmethod
|
|
31
|
-
def
|
|
52
|
+
def from_sparql_query_solution(
|
|
53
|
+
cls, query_solution: og.QuerySolution
|
|
54
|
+
) -> "Namespace":
|
|
32
55
|
"""
|
|
33
|
-
Creates a Namespace from a
|
|
56
|
+
Creates a Namespace from a binding.
|
|
34
57
|
|
|
35
58
|
Args:
|
|
36
|
-
binding (Mapping[Variable, Identifier]): The
|
|
59
|
+
binding (Mapping[Variable, Identifier]): The binding.
|
|
37
60
|
|
|
38
61
|
Returns:
|
|
39
62
|
Namespace: The created Namespace.
|
|
40
63
|
"""
|
|
41
|
-
prefix =
|
|
42
|
-
namespace =
|
|
64
|
+
prefix: og.Literal = query_solution[og.Variable("prefix")]
|
|
65
|
+
namespace: og.NamedNode = query_solution[og.Variable("namespace")]
|
|
43
66
|
return cls(
|
|
44
|
-
prefix=prefix,
|
|
45
|
-
namespace=namespace,
|
|
67
|
+
prefix=prefix.value,
|
|
68
|
+
namespace=namespace.value,
|
|
46
69
|
)
|
|
47
70
|
|
|
48
71
|
def __str__(self):
|
|
@@ -85,7 +108,7 @@ class Namespace:
|
|
|
85
108
|
Returns:
|
|
86
109
|
IRI: The IRI for the term.
|
|
87
110
|
"""
|
|
88
|
-
return
|
|
111
|
+
return self._namespace.term(name)
|
|
89
112
|
|
|
90
113
|
def __getitem__(self, item: str) -> IRI:
|
|
91
114
|
"""
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
import pyoxigraph as og
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class RepositoryMetadata:
|
|
8
|
+
"""
|
|
9
|
+
Represents a repository metadata RDF4J.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
id: str # The repository identifier
|
|
13
|
+
uri: str # The full URI to the repository
|
|
14
|
+
title: str # A human-readable title (currently reusing id)
|
|
15
|
+
readable: bool # Whether the repository is readable
|
|
16
|
+
writable: bool # Whether the repository is writable
|
|
17
|
+
|
|
18
|
+
def __str__(self):
|
|
19
|
+
"""
|
|
20
|
+
Returns a string representation of the RepositoryMetadata.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
str: A string representation of the RepositoryMetadata.
|
|
24
|
+
"""
|
|
25
|
+
return f"Repository(id={self.id}, title={self.title}, uri={self.uri})"
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def from_sparql_query_solution(
|
|
29
|
+
cls, query_solution: og.QuerySolution
|
|
30
|
+
) -> "RepositoryMetadata":
|
|
31
|
+
"""
|
|
32
|
+
Create a RepositoryMetadata instance from a SPARQL query result.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
query_solution (og.QuerySolution): The SPARQL query result.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
RepositoryMetadata: The RepositoryMetadata instance.
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ValueError: If the query solution is missing required fields.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
# Construct and return the Repository object
|
|
45
|
+
if query_solution["id"] is None:
|
|
46
|
+
raise ValueError("id is required")
|
|
47
|
+
if query_solution["uri"] is None:
|
|
48
|
+
raise ValueError("uri is required")
|
|
49
|
+
if query_solution["title"] is None:
|
|
50
|
+
raise ValueError("title is required")
|
|
51
|
+
if query_solution["readable"] is None:
|
|
52
|
+
raise ValueError("readable is required")
|
|
53
|
+
if query_solution["writable"] is None:
|
|
54
|
+
raise ValueError("writable is required")
|
|
55
|
+
|
|
56
|
+
return cls(
|
|
57
|
+
id=query_solution["id"].value,
|
|
58
|
+
uri=query_solution["uri"].value,
|
|
59
|
+
title=query_solution["title"].value,
|
|
60
|
+
readable=bool(query_solution["readable"].value),
|
|
61
|
+
writable=bool(query_solution["writable"].value),
|
|
62
|
+
)
|