rdf4j-python 0.1.0__tar.gz → 0.1.1a0__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.1a0/LICENSE +28 -0
- rdf4j_python-0.1.1a0/PKG-INFO +77 -0
- rdf4j_python-0.1.1a0/README.md +66 -0
- {rdf4j_python-0.1.0 → rdf4j_python-0.1.1a0}/pyproject.toml +1 -1
- {rdf4j_python-0.1.0 → rdf4j_python-0.1.1a0}/rdf4j_python/__init__.py +3 -3
- rdf4j_python-0.1.1a0/rdf4j_python/_driver/__init__.py +7 -0
- {rdf4j_python-0.1.0 → rdf4j_python-0.1.1a0}/rdf4j_python/_driver/_async_rdf4j_db.py +10 -8
- rdf4j_python-0.1.1a0/rdf4j_python/_driver/_async_repository.py +109 -0
- rdf4j_python-0.1.1a0/rdf4j_python/exception/repo_exception.py +13 -0
- rdf4j_python-0.1.1a0/rdf4j_python/model/__init__.py +16 -0
- rdf4j_python-0.1.1a0/rdf4j_python/model/_namespace.py +56 -0
- rdf4j_python-0.1.1a0/rdf4j_python/model/_repository_config.py +899 -0
- rdf4j_python-0.1.0/rdf4j_python/model/repository.py → rdf4j_python-0.1.1a0/rdf4j_python/model/_repository_info.py +3 -3
- rdf4j_python-0.1.1a0/rdf4j_python.egg-info/PKG-INFO +77 -0
- {rdf4j_python-0.1.0 → rdf4j_python-0.1.1a0}/rdf4j_python.egg-info/SOURCES.txt +7 -2
- rdf4j_python-0.1.0/tests/test_repository.py → rdf4j_python-0.1.1a0/tests/test_client.py +17 -4
- rdf4j_python-0.1.1a0/tests/test_rdf4j_repo.py +124 -0
- rdf4j_python-0.1.0/PKG-INFO +0 -8
- rdf4j_python-0.1.0/README.md +0 -0
- rdf4j_python-0.1.0/rdf4j_python/_driver/__init__.py +0 -7
- rdf4j_python-0.1.0/rdf4j_python/_driver/_async_repository.py +0 -51
- rdf4j_python-0.1.0/rdf4j_python/exception/repo_exception.py +0 -4
- rdf4j_python-0.1.0/rdf4j_python.egg-info/PKG-INFO +0 -8
- {rdf4j_python-0.1.0 → rdf4j_python-0.1.1a0}/rdf4j_python/_client/__init__.py +0 -0
- {rdf4j_python-0.1.0 → rdf4j_python-0.1.1a0}/rdf4j_python/_client/_client.py +0 -0
- {rdf4j_python-0.1.0 → rdf4j_python-0.1.1a0}/rdf4j_python/model/_base_model.py +0 -0
- {rdf4j_python-0.1.0 → rdf4j_python-0.1.1a0}/rdf4j_python/utils/__init__.py +0 -0
- {rdf4j_python-0.1.0 → rdf4j_python-0.1.1a0}/rdf4j_python/utils/const.py +0 -0
- {rdf4j_python-0.1.0 → rdf4j_python-0.1.1a0}/rdf4j_python.egg-info/dependency_links.txt +0 -0
- {rdf4j_python-0.1.0 → rdf4j_python-0.1.1a0}/rdf4j_python.egg-info/requires.txt +0 -0
- {rdf4j_python-0.1.0 → rdf4j_python-0.1.1a0}/rdf4j_python.egg-info/top_level.txt +0 -0
- {rdf4j_python-0.1.0 → rdf4j_python-0.1.1a0}/setup.cfg +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Chengxu Bian
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rdf4j-python
|
|
3
|
+
Version: 0.1.1a0
|
|
4
|
+
Summary: The Python client for RDF4J
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: httpx>=0.28.1
|
|
9
|
+
Requires-Dist: rdflib>=7.1.4
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# 🐍 rdf4j-python
|
|
13
|
+
|
|
14
|
+
**A Pythonic interface to the powerful Java-based [Eclipse RDF4J](https://rdf4j.org/) framework.**
|
|
15
|
+
|
|
16
|
+
> ⚠️ **Note:** This project is currently under active development and considered **experimental**. Interfaces may change. Use with caution in production environments—and feel free to help shape its future!
|
|
17
|
+
|
|
18
|
+
✅ **Supports both asynchronous (`async/await`) and synchronous programming styles.**
|
|
19
|
+
|
|
20
|
+
## 🌐 Overview
|
|
21
|
+
|
|
22
|
+
`rdf4j-python` bridges the gap between Python applications and the [Eclipse RDF4J](https://rdf4j.org/) framework, enabling seamless interaction with RDF4J repositories directly from Python. This integration allows developers to leverage RDF4J's robust capabilities for managing RDF data and executing SPARQL queries without leaving the Python ecosystem.
|
|
23
|
+
|
|
24
|
+
## 🚀 Features
|
|
25
|
+
|
|
26
|
+
- **Seamless Integration**: Interact with RDF4J repositories using Pythonic constructs.
|
|
27
|
+
- **SPARQL Support**: Execute SPARQL queries and updates effortlessly.
|
|
28
|
+
- **Repository Management**: Create, access, and manage RDF4J repositories programmatically.
|
|
29
|
+
- **Data Handling**: Add and retrieve RDF triples with ease.
|
|
30
|
+
- **Format Flexibility**: Support for various RDF serialization formats.
|
|
31
|
+
|
|
32
|
+
## 📦 Installation
|
|
33
|
+
|
|
34
|
+
Install via pip:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install rdf4j-python
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 🧪 Usage (Async)
|
|
41
|
+
|
|
42
|
+
Here's a basic example of how to use `rdf4j-python` to create an in-memory sail repository
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from rdf4j_python import AsyncRdf4j
|
|
46
|
+
from rdf4j_python.model import MemoryStoreConfig, RepositoryConfig
|
|
47
|
+
from rdf4j_python.utils.const import Rdf4jContentType
|
|
48
|
+
|
|
49
|
+
async with AsyncRdf4j("http://localhost:19780/rdf4j-server") as db:
|
|
50
|
+
repo_config = (
|
|
51
|
+
RepositoryConfig.builder_with_sail_repository(
|
|
52
|
+
MemoryStoreConfig.Builder().persist(False).build(),
|
|
53
|
+
)
|
|
54
|
+
.repo_id("example-repo")
|
|
55
|
+
.title("Example Repository")
|
|
56
|
+
.build()
|
|
57
|
+
)
|
|
58
|
+
await db.create_repository(
|
|
59
|
+
repository_id=repo_config.repo_id,
|
|
60
|
+
rdf_config_data=repo_config.to_turtle(),
|
|
61
|
+
content_type=Rdf4jContentType.TURTLE,
|
|
62
|
+
)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
For more detailed examples, refer to the [examples](https://github.com/odysa/rdf4j-python/tree/main/examples) directory.
|
|
66
|
+
|
|
67
|
+
## 🤝 Contributing
|
|
68
|
+
|
|
69
|
+
We welcome contributions and feedback! If you'd like to help improve this project:
|
|
70
|
+
|
|
71
|
+
- Fork the repo and submit a pull request
|
|
72
|
+
- Open an issue for bugs, feature ideas, or discussions
|
|
73
|
+
- ⭐ Star the repo if you find it useful!
|
|
74
|
+
|
|
75
|
+
## 📄 License
|
|
76
|
+
|
|
77
|
+
This project is licensed under the MIT License. See the [LICENSE](https://github.com/odysa/rdf4j-python/blob/main/LICENSE) file for details.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# 🐍 rdf4j-python
|
|
2
|
+
|
|
3
|
+
**A Pythonic interface to the powerful Java-based [Eclipse RDF4J](https://rdf4j.org/) framework.**
|
|
4
|
+
|
|
5
|
+
> ⚠️ **Note:** This project is currently under active development and considered **experimental**. Interfaces may change. Use with caution in production environments—and feel free to help shape its future!
|
|
6
|
+
|
|
7
|
+
✅ **Supports both asynchronous (`async/await`) and synchronous programming styles.**
|
|
8
|
+
|
|
9
|
+
## 🌐 Overview
|
|
10
|
+
|
|
11
|
+
`rdf4j-python` bridges the gap between Python applications and the [Eclipse RDF4J](https://rdf4j.org/) framework, enabling seamless interaction with RDF4J repositories directly from Python. This integration allows developers to leverage RDF4J's robust capabilities for managing RDF data and executing SPARQL queries without leaving the Python ecosystem.
|
|
12
|
+
|
|
13
|
+
## 🚀 Features
|
|
14
|
+
|
|
15
|
+
- **Seamless Integration**: Interact with RDF4J repositories using Pythonic constructs.
|
|
16
|
+
- **SPARQL Support**: Execute SPARQL queries and updates effortlessly.
|
|
17
|
+
- **Repository Management**: Create, access, and manage RDF4J repositories programmatically.
|
|
18
|
+
- **Data Handling**: Add and retrieve RDF triples with ease.
|
|
19
|
+
- **Format Flexibility**: Support for various RDF serialization formats.
|
|
20
|
+
|
|
21
|
+
## 📦 Installation
|
|
22
|
+
|
|
23
|
+
Install via pip:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install rdf4j-python
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 🧪 Usage (Async)
|
|
30
|
+
|
|
31
|
+
Here's a basic example of how to use `rdf4j-python` to create an in-memory sail repository
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from rdf4j_python import AsyncRdf4j
|
|
35
|
+
from rdf4j_python.model import MemoryStoreConfig, RepositoryConfig
|
|
36
|
+
from rdf4j_python.utils.const import Rdf4jContentType
|
|
37
|
+
|
|
38
|
+
async with AsyncRdf4j("http://localhost:19780/rdf4j-server") as db:
|
|
39
|
+
repo_config = (
|
|
40
|
+
RepositoryConfig.builder_with_sail_repository(
|
|
41
|
+
MemoryStoreConfig.Builder().persist(False).build(),
|
|
42
|
+
)
|
|
43
|
+
.repo_id("example-repo")
|
|
44
|
+
.title("Example Repository")
|
|
45
|
+
.build()
|
|
46
|
+
)
|
|
47
|
+
await db.create_repository(
|
|
48
|
+
repository_id=repo_config.repo_id,
|
|
49
|
+
rdf_config_data=repo_config.to_turtle(),
|
|
50
|
+
content_type=Rdf4jContentType.TURTLE,
|
|
51
|
+
)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
For more detailed examples, refer to the [examples](https://github.com/odysa/rdf4j-python/tree/main/examples) directory.
|
|
55
|
+
|
|
56
|
+
## 🤝 Contributing
|
|
57
|
+
|
|
58
|
+
We welcome contributions and feedback! If you'd like to help improve this project:
|
|
59
|
+
|
|
60
|
+
- Fork the repo and submit a pull request
|
|
61
|
+
- Open an issue for bugs, feature ideas, or discussions
|
|
62
|
+
- ⭐ Star the repo if you find it useful!
|
|
63
|
+
|
|
64
|
+
## 📄 License
|
|
65
|
+
|
|
66
|
+
This project is licensed under the MIT License. See the [LICENSE](https://github.com/odysa/rdf4j-python/blob/main/LICENSE) file for details.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from ._client import AsyncApiClient, SyncApiClient
|
|
2
|
-
from ._driver import
|
|
2
|
+
from ._driver import AsyncRdf4j, AsyncRdf4JRepository
|
|
3
3
|
|
|
4
4
|
__all__ = [
|
|
5
5
|
"AsyncApiClient",
|
|
6
6
|
"SyncApiClient",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"AsyncRdf4j",
|
|
8
|
+
"AsyncRdf4JRepository",
|
|
9
9
|
]
|
|
@@ -8,13 +8,13 @@ from rdf4j_python.exception.repo_exception import (
|
|
|
8
8
|
RepositoryCreationException,
|
|
9
9
|
RepositoryDeletionException,
|
|
10
10
|
)
|
|
11
|
-
from rdf4j_python.model.
|
|
11
|
+
from rdf4j_python.model._repository_info import RepositoryMetadata
|
|
12
12
|
from rdf4j_python.utils.const import Rdf4jContentType
|
|
13
13
|
|
|
14
|
-
from ._async_repository import
|
|
14
|
+
from ._async_repository import AsyncRdf4JRepository
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class
|
|
17
|
+
class AsyncRdf4j:
|
|
18
18
|
_client: AsyncApiClient
|
|
19
19
|
_base_url: str
|
|
20
20
|
|
|
@@ -33,7 +33,7 @@ class AsyncRdf4jDB:
|
|
|
33
33
|
response.raise_for_status()
|
|
34
34
|
return response.text
|
|
35
35
|
|
|
36
|
-
async def list_repositories(self) -> list[
|
|
36
|
+
async def list_repositories(self) -> list[RepositoryMetadata]:
|
|
37
37
|
"""
|
|
38
38
|
List all RDF4J repositories.
|
|
39
39
|
|
|
@@ -48,24 +48,25 @@ class AsyncRdf4jDB:
|
|
|
48
48
|
)
|
|
49
49
|
|
|
50
50
|
return [
|
|
51
|
-
|
|
51
|
+
RepositoryMetadata.from_rdflib_binding(binding)
|
|
52
|
+
for binding in result.bindings
|
|
52
53
|
]
|
|
53
54
|
|
|
54
|
-
async def get_repository(self, repository_id: str) ->
|
|
55
|
+
async def get_repository(self, repository_id: str) -> AsyncRdf4JRepository:
|
|
55
56
|
"""
|
|
56
57
|
Get an AsyncRepository instance for the specified repository ID.
|
|
57
58
|
|
|
58
59
|
:param repository_id: The ID of the repository.
|
|
59
60
|
:return: An instance of AsyncRepository.
|
|
60
61
|
"""
|
|
61
|
-
return
|
|
62
|
+
return AsyncRdf4JRepository(self._client, repository_id)
|
|
62
63
|
|
|
63
64
|
async def create_repository(
|
|
64
65
|
self,
|
|
65
66
|
repository_id: str,
|
|
66
67
|
rdf_config_data: str,
|
|
67
68
|
content_type: Union[Rdf4jContentType, str] = Rdf4jContentType.TURTLE,
|
|
68
|
-
):
|
|
69
|
+
) -> AsyncRdf4JRepository:
|
|
69
70
|
"""
|
|
70
71
|
Create a new RDF4J repository.
|
|
71
72
|
|
|
@@ -86,6 +87,7 @@ class AsyncRdf4jDB:
|
|
|
86
87
|
raise RepositoryCreationException(
|
|
87
88
|
f"Repository creation failed: {response.status_code} - {response.text}"
|
|
88
89
|
)
|
|
90
|
+
return AsyncRdf4JRepository(self._client, repository_id)
|
|
89
91
|
|
|
90
92
|
async def delete_repository(self, repository_id: str):
|
|
91
93
|
"""
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import rdflib
|
|
3
|
+
|
|
4
|
+
from rdf4j_python import AsyncApiClient
|
|
5
|
+
from rdf4j_python.exception.repo_exception import (
|
|
6
|
+
NamespaceException,
|
|
7
|
+
RepositoryInternalException,
|
|
8
|
+
RepositoryNotFoundException,
|
|
9
|
+
)
|
|
10
|
+
from rdf4j_python.model._namespace import Namespace
|
|
11
|
+
from rdf4j_python.utils.const import Rdf4jContentType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AsyncRdf4JRepository:
|
|
15
|
+
def __init__(self, client: AsyncApiClient, repository_id: str):
|
|
16
|
+
self._client = client
|
|
17
|
+
self._repository_id = repository_id
|
|
18
|
+
|
|
19
|
+
async def query(
|
|
20
|
+
self,
|
|
21
|
+
sparql_query: str,
|
|
22
|
+
infer: bool = True,
|
|
23
|
+
accept: Rdf4jContentType = Rdf4jContentType.SPARQL_RESULTS_JSON,
|
|
24
|
+
):
|
|
25
|
+
path = f"/repositories/{self._repository_id}"
|
|
26
|
+
params = {"query": sparql_query, "infer": str(infer).lower()}
|
|
27
|
+
headers = {"Accept": accept.value}
|
|
28
|
+
response = await self._client.get(path, params=params, headers=headers)
|
|
29
|
+
self._handle_repo_not_found_exception(response)
|
|
30
|
+
if "json" in response.headers.get("Content-Type", ""):
|
|
31
|
+
return response.json()
|
|
32
|
+
return response.text
|
|
33
|
+
|
|
34
|
+
async def update(self, sparql_update: str):
|
|
35
|
+
path = f"/repositories/{self._repository_id}/statements"
|
|
36
|
+
headers = {"Content-Type": Rdf4jContentType.SPARQL_UPDATE.value}
|
|
37
|
+
response = await self._client.post(path, data=sparql_update, headers=headers)
|
|
38
|
+
self._handle_repo_not_found_exception(response)
|
|
39
|
+
response.raise_for_status()
|
|
40
|
+
|
|
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
|
+
async def get_namespaces(self):
|
|
51
|
+
path = f"/repositories/{self._repository_id}/namespaces"
|
|
52
|
+
headers = {"Accept": Rdf4jContentType.SPARQL_RESULTS_JSON}
|
|
53
|
+
response = await self._client.get(path, headers=headers)
|
|
54
|
+
|
|
55
|
+
result = rdflib.query.Result.parse(
|
|
56
|
+
response, format=Rdf4jContentType.SPARQL_RESULTS_JSON
|
|
57
|
+
)
|
|
58
|
+
self._handle_repo_not_found_exception(response)
|
|
59
|
+
return [Namespace.from_rdflib_binding(binding) for binding in result.bindings]
|
|
60
|
+
|
|
61
|
+
async def set_namespace(self, prefix: str, namespace: str):
|
|
62
|
+
path = f"/repositories/{self._repository_id}/namespaces/{prefix}"
|
|
63
|
+
headers = {"Content-Type": Rdf4jContentType.NTRIPLES.value}
|
|
64
|
+
response = await self._client.put(path, content=namespace, headers=headers)
|
|
65
|
+
self._handle_repo_not_found_exception(response)
|
|
66
|
+
if response.status_code != httpx.codes.NO_CONTENT:
|
|
67
|
+
raise NamespaceException(f"Failed to set namespace: {response.text}")
|
|
68
|
+
|
|
69
|
+
async def get_namespace(self, prefix: str) -> Namespace:
|
|
70
|
+
path = f"/repositories/{self._repository_id}/namespaces/{prefix}"
|
|
71
|
+
headers = {"Accept": Rdf4jContentType.NTRIPLES.value}
|
|
72
|
+
response = await self._client.get(path, headers=headers)
|
|
73
|
+
self._handle_repo_not_found_exception(response)
|
|
74
|
+
|
|
75
|
+
if response.status_code != httpx.codes.OK:
|
|
76
|
+
raise NamespaceException(f"Failed to get namespace: {response.text}")
|
|
77
|
+
|
|
78
|
+
return Namespace(prefix, response.text)
|
|
79
|
+
|
|
80
|
+
async def delete_namespace(self, prefix: str):
|
|
81
|
+
path = f"/repositories/{self._repository_id}/namespaces/{prefix}"
|
|
82
|
+
response = await self._client.delete(path)
|
|
83
|
+
self._handle_repo_not_found_exception(response)
|
|
84
|
+
response.raise_for_status()
|
|
85
|
+
|
|
86
|
+
async def size(self) -> int:
|
|
87
|
+
path = f"/repositories/{self._repository_id}/size"
|
|
88
|
+
response = await self._client.get(path)
|
|
89
|
+
self._handle_repo_not_found_exception(response)
|
|
90
|
+
|
|
91
|
+
if response.status_code != httpx.codes.OK:
|
|
92
|
+
raise RepositoryInternalException(f"Failed to get size: {response.text}")
|
|
93
|
+
|
|
94
|
+
return int(response.text.strip())
|
|
95
|
+
|
|
96
|
+
async def add_statement(self, subject: str, predicate: str, object: str):
|
|
97
|
+
path = f"/repositories/{self._repository_id}/statements"
|
|
98
|
+
headers = {"Content-Type": Rdf4jContentType.NTRIPLES.value}
|
|
99
|
+
response = await self._client.post(
|
|
100
|
+
path, data=f"{subject} {predicate} {object}.", headers=headers
|
|
101
|
+
)
|
|
102
|
+
self._handle_repo_not_found_exception(response)
|
|
103
|
+
response.raise_for_status()
|
|
104
|
+
|
|
105
|
+
def _handle_repo_not_found_exception(self, response: httpx.Response):
|
|
106
|
+
if response.status_code == httpx.codes.NOT_FOUND:
|
|
107
|
+
raise RepositoryNotFoundException(
|
|
108
|
+
f"Repository {self._repository_id} not found"
|
|
109
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class RepositoryCreationException(Exception): ...
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RepositoryDeletionException(Exception): ...
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NamespaceException(Exception): ...
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RepositoryNotFoundException(Exception): ...
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RepositoryInternalException(Exception): ...
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from ._namespace import IRI, Namespace
|
|
2
|
+
from ._repository_config import (
|
|
3
|
+
MemoryStoreConfig,
|
|
4
|
+
NativeStoreConfig,
|
|
5
|
+
RepositoryConfig,
|
|
6
|
+
)
|
|
7
|
+
from ._repository_info import RepositoryMetadata
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"IRI",
|
|
11
|
+
"Namespace",
|
|
12
|
+
"RepositoryConfig",
|
|
13
|
+
"MemoryStoreConfig",
|
|
14
|
+
"NativeStoreConfig",
|
|
15
|
+
"RepositoryMetadata",
|
|
16
|
+
]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from typing import Mapping
|
|
2
|
+
|
|
3
|
+
from rdflib import URIRef
|
|
4
|
+
from rdflib.namespace import Namespace as RdflibNamespace
|
|
5
|
+
from rdflib.term import Identifier, Variable
|
|
6
|
+
|
|
7
|
+
from ._base_model import _BaseModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class IRI(URIRef): ...
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Namespace:
|
|
14
|
+
_prefix: str
|
|
15
|
+
_namespace: RdflibNamespace
|
|
16
|
+
|
|
17
|
+
def __init__(self, prefix: str, namespace: str):
|
|
18
|
+
self._prefix = prefix
|
|
19
|
+
self._namespace = RdflibNamespace(namespace)
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def from_rdflib_binding(cls, binding: Mapping[Variable, Identifier]) -> "Namespace":
|
|
23
|
+
prefix = _BaseModel.get_literal(binding, "prefix", "")
|
|
24
|
+
namespace = _BaseModel.get_literal(binding, "namespace", "")
|
|
25
|
+
return cls(
|
|
26
|
+
prefix=prefix,
|
|
27
|
+
namespace=namespace,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def __str__(self):
|
|
31
|
+
return f"{self._prefix}: {self._namespace}"
|
|
32
|
+
|
|
33
|
+
def __repr__(self):
|
|
34
|
+
return f"Namespace(prefix={self._prefix}, namespace={self._namespace})"
|
|
35
|
+
|
|
36
|
+
def __contains__(self, item: str) -> bool:
|
|
37
|
+
return item in self._namespace
|
|
38
|
+
|
|
39
|
+
def term(self, name: str) -> IRI:
|
|
40
|
+
return IRI(self._namespace.term(name))
|
|
41
|
+
|
|
42
|
+
def __getitem__(self, item: str) -> IRI:
|
|
43
|
+
return self.term(item)
|
|
44
|
+
|
|
45
|
+
def __getattr__(self, item: str) -> IRI:
|
|
46
|
+
if item.startswith("__"):
|
|
47
|
+
raise AttributeError
|
|
48
|
+
return self.term(item)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def namespace(self) -> IRI:
|
|
52
|
+
return IRI(self._namespace)
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def prefix(self) -> str:
|
|
56
|
+
return self._prefix
|