kapps-triplestore-interface
This library turns SPARQL queries and updates against a running GraphDB triplestore into callable Python methods. Three research projects run it in production.
Installation
pip install kapps-triplestore-interface
Or, from a clone of this repository:
uv sync
Getting Started
connect() gives you a working, authenticated store. It takes where the store is, an auth
provider for your identity:
from kapps_triplestore_interface import GDBTokenAuth, TripleStoreEndpoint, connect
my_db = connect(
TripleStoreEndpoint(url="<your_graph_db_url>", dataset="<your_repository_id>"),
GDBTokenAuth("<your_graph_db_user>", "<your_graph_db_password>"),
)
# Or everything from TRIPLESTORE_URL, TRIPLESTORE_DATASET, TRIPLESTORE_USERNAME and
# TRIPLESTORE_PASSWORD:
my_db = connect(TripleStoreEndpoint.from_env(), GDBTokenAuth.from_env())
connect() logs in and sends one probe to the dataset. So a broken configuration fails at the
start and not at the first request. A write by an identity that may only read fails at the
first write, as TripleStoreAccessError. The error types are those of the section "When a call
fails". The endpoint holds no secret, so you can log it. The provider renews its token before
it expires, and logs in again once when the store answers 401. The GRAPHDB_* variables are
read as a deprecated fallback of the TRIPLESTORE_* ones; the API reference lists the
providers and the variables.
The store contract
Code above this interface types its store as TripleStore, which connect() returns. It
declares the operations that the object-graph mapper, the middleware and the use cases call on
a store, measured in their code, and no other; its docstring lists them. The GraphDB adapter,
GraphDB, implements each one. A test double satisfies the contract without inheriting from
it.
The store's registries
A store keeps two registries that SPARQL does not cover. The contract has an operation for each, so that no code above the interface calls a vendor's endpoint for them:
| Operation | What it does |
|---|---|
prefixes_get() |
Each prefix of the dataset, with the namespace IRI that it stands for, as a dict. The empty prefix is the key "". |
prefix_set(prefix, iri) |
Lets prefix stand for the namespace iri. A prefix that the dataset has already stands for iri afterwards. |
prefix_delete(prefix) |
Removes prefix. Every other prefix stays. |
shapes_set(data, content_type) |
Replaces the SHACL shapes that the store validates the dataset against with the document data, of the media type content_type, for example text/turtle. An empty document removes every shape. |
"" is the empty prefix in each prefix operation. A failed operation raises; none returns a
flag.
A store that has no such registry raises TripleStoreNotSupportedError, and an operation
is never a silent no-op. An adapter that inherits TripleStore and does not implement a
registry raises it from the contract's own operations.
shapes_set on a dataset without SHACL validation raises TripleStoreNotSupportedError,
and writes nothing. GraphDB turns SHACL validation on only when a repository is created; on a
repository without it, the shapes would be stored as plain data that validates nothing. The
GraphDB adapter reads the repository's configuration to tell, and it refuses also when the
configuration does not say. On a dataset with SHACL validation it replaces the shapes graph
with data in one request, which the store applies whole or not at all. If the store refuses
it, because the data of the dataset violates the new shapes, shapes_set raises
ShaclValidationError, and the old shapes stay.
What a write promises
The store answers a SPARQL update with success also when the update changes nothing. So the five write functions that have a precondition send it inside the update itself:
| function | precondition with check_exist=True (the default) |
|---|---|
triples_add |
None of the triples exists yet. Triples that share a blank node are one structure. |
triples_delete, triple_delete |
All of the triples exist. |
triples_update, triple_update |
All of the old triples exist. |
triples_add and triples_update also take absent_subjects: IRIs that must not yet be the
subject of a triple in the target graph. A subject that occurs only as an object passes.
-
A normal return means "applied". The store found the precondition true and committed the change. The function returns
True. -
A false precondition raises
PreconditionFailedError, and nothing is written. The error carries.triples, the triples that the precondition named, and.named_graph. -
The scope is the explicit triples of the target graph. The target graph is
named_graph, or the default graph without the named graphs. A read (triples_get,query) sees all graphs and the inferred triples, but a delete cannot remove an inferred triple. So the precondition refuses an old triple that is only inferred. -
A triple that is both explicit and inferred stays visible after a delete, because a delete clears only the explicit flag.
-
Concurrent writers. The store isolates only commits ("read committed"). Another writer can commit between the evaluation of the precondition and the commit of the write. Measured with two threads that replaced the same triple at the same time:
- GraphDB Free 10.8.1 on a local machine: both writes applied in 50 of 50 rounds. With a start offset of 20 ms, both applied in 1 of 30 rounds.
- GraphDB 10.8.5 Enterprise on a remote server: both writes applied in 0 of 50 rounds, in the default graph and in a named graph.
The rate depends on the store and on its load, so a rate of 0 does not mean that the race cannot occur. An invariant that must hold under concurrent writers belongs in a SHACL shape: the store checks shapes at the commit.
-
check_exist=Falsesends the write without a precondition and returnsTrue. An unchecked update still applies only when its old triples match. -
The receipt. A checked write also writes one receipt triple into the graph
urn:x-kapps:write-receipts, in the same update. The function reads the receipt and then removes it. If the process stops between the write and the removal, the receipt stays, with its time. -
A write that gets no answer (for example a timeout, or a connection that the store closed) raises
TripleStoreUnavailableError, chained to the error of the HTTP client. The write is not sent again, and its receipt is not read: the store can still commit the write after that. A retry by the caller is safe: a checked write refuses a second application withPreconditionFailedError, and an insert or an import repeats as a no-op, because a graph is a set. The exceptions are blank nodes, which a second insert creates again, and a raw update that the caller writes itself.
When the store refuses a write
A repository with SHACL validation checks a write when it commits it. If the data would
violate a shape, the store commits nothing of the write.
GraphDB.query then raises ShaclValidationError, and so do the functions that send their
update through it, for example triples_add and triples_update. graph_clear raises it too,
when the store refuses a clear because the remaining data would violate a shape. The error's
report is the store's validation report, unchanged:
from kapps_triplestore_interface import ShaclValidationError
try:
my_db.triples_add([(subject, predicate, obj)])
except ShaclValidationError as refused:
print(refused.report)
The client logs a refusal only at DEBUG, so your code decides how loud it is. When
GraphDB.query or graph_clear gets any other error answer from the store, it logs it at ERROR
and raises a different type. To catch any error from this package, catch TripleStoreInterfaceError from
kapps_triplestore_interface.exceptions.
When a call fails
Each error type tells your code one action, so you choose with except and do not read the
message. A message holds the store's answer unchanged.
| Type | What happened | Action |
|---|---|---|
TripleStoreResponseError |
The store answered with an error status that no subtype below covers. status is that status. |
Repair |
AuthenticationError |
The store answered 401: it did not accept the credentials. | Repair |
TripleStoreAccessError |
The store answered 403: the identity has no access to the dataset. | Repair |
ShaclValidationError |
The store's shapes refused the write. report is the store's report. |
Refused |
TripleStoreUnavailableError |
The store cannot be reached, it did not answer in time, or the connection dropped. | Retry |
PreconditionFailedError |
The precondition of a checked write was false, and nothing was written. | Refresh |
TripleStoreNotSupportedError |
The store, or the dataset, cannot do the operation. | Fix the call |
InvalidDatasetError |
The dataset does not exist on the store. | Repair |
InvalidQueryError, InvalidInputError, InvalidIRIError |
The call is incorrect. | Fix the call |
AuthenticationError, TripleStoreAccessError and ShaclValidationError are subtypes of
TripleStoreResponseError, and each carries the store's status.
Listing the datasets (datasets_get) and fetching a graph (graph_get) raise on an error
answer. They never return None.
Running Tests
The test suite expects a running GraphDB instance and uses a dedicated test repository.
Required environment variables for tests:
GRAPHDB_URLGRAPHDB_USERNAMEGRAPHDB_PASSWORDGRAPHDB_TEST_REPOSITORY
Example:
export GRAPHDB_URL="http://localhost:7200"
export GRAPHDB_USERNAME="admin"
export GRAPHDB_PASSWORD="root"
export GRAPHDB_TEST_REPOSITORY="test-repo"
uv run pytest tests -v
The test of shapes_set on a dataset without SHACL validation needs a second repository on the
same store, created without SHACL validation, named by GRAPHDB_TEST_REPOSITORY_WITHOUT_SHACL.
Without it, that test skips. The other tests of the shapes need SHACL validation in the test
repository, and they leave it with no shapes.
Note: the suite reads its dataset from GRAPHDB_TEST_REPOSITORY. An application builds its
store from TRIPLESTORE_DATASET, as in "Getting Started", and never from that variable.
Contributing
Development happens in a private repository at KIT. This GitHub repository is a publish target. A pull request opened here cannot be merged back. Raise an issue instead, or contact the maintainers.
Origin
This is a renamed fork of
JaFeKl/graph_db_interface by Jan-Felix Klein,
last released as graph-db-interface 2.0.1 on
PyPI under an MIT license. It is maintained here, and changes since then are listed in
CHANGELOG.md.
Migration from graph-db-interface
- Change your dependency from
graph-db-interfacetokapps-triplestore-interface. - Change imports from
from graph_db_interface import ...tofrom kapps_triplestore_interface import .... - If you catch it, change
GraphDBInterfaceErrortoTripleStoreInterfaceError.
License
The package is licensed under the MIT license.
Acknowledgements
This package is developed as part of the INF subproject of the CRC 1574: Circular Factory for the Perpetual Product. This work is therefore supported by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) [grant-number: SFB-1574-471687386].
Metadata
Release files for kapps-triplestore-interface 2.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kapps_triplestore_interface-2.2.1.tar.gz | 172.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kapps_triplestore_interface-2.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 238.4 kB
Release files / kapps_triplestore_interface-2.2.1.tar.gz
| Download URL | kapps_triplestore_interface-2.2.1.tar.gz |
|---|---|
| Size | 172.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
48dce4eb20b089f310879e03da336bc07939aeed74dfb916915b78575beba28c
|
|
BLAKE2b-256 checksum How to use checksums |
67bc922f7995ca76ab858b094a9fd9960c927ef787ff6b2c36d73e6e22545bd6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logRelease files / kapps_triplestore_interface-2.2.1-py3-none-any.whl
| Download URL | kapps_triplestore_interface-2.2.1-py3-none-any.whl |
|---|---|
| Size | 65.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cfee9c78391a79faae3ce7986a0dba493a4390237965149eeded2d24c9f8b022
|
|
BLAKE2b-256 checksum How to use checksums |
77693faf1433f6aef5e15f0a1aa9f063db2f0b94b29ce378bcb541c168f75de0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency log